Skip to content

Commit 0a225a4

Browse files
Koromixmarco-ippolito
authored andcommitted
node-api: copy external type tags when they are set
In order to adapt to V8 changes regarding storing private properties on Externals, ExternalWrapper objects were introduced in #51149. However, this new code stores the type tag pointer and not the 128-bit value inside. This breaks some pre-existing code that were making temporary tags. It also means that unloading the module will cause existing External objects to have a tag pointer that points nowhere (use-after-free bug). Change ExternalWrapper to store tags by value to fix this regression. PR-URL: #52426 Reviewed-By: Gabriel Schulhof <gabrielschulhof@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
1 parent b4d7032 commit 0a225a4

File tree

2 files changed

+33
-9
lines changed

2 files changed

+33
-9
lines changed

src/js_native_api_v8.cc

+7-6
Original file line numberDiff line numberDiff line change
@@ -858,23 +858,24 @@ class ExternalWrapper {
858858
void* Data() { return data_; }
859859

860860
bool TypeTag(const napi_type_tag* type_tag) {
861-
if (type_tag_ != nullptr) {
861+
if (has_tag_) {
862862
return false;
863863
}
864-
type_tag_ = type_tag;
864+
type_tag_ = *type_tag;
865+
has_tag_ = true;
865866
return true;
866867
}
867868

868869
bool CheckTypeTag(const napi_type_tag* type_tag) {
869-
return type_tag == type_tag_ ||
870-
(type_tag_ && type_tag->lower == type_tag_->lower &&
871-
type_tag->upper == type_tag_->upper);
870+
return has_tag_ && type_tag->lower == type_tag_.lower &&
871+
type_tag->upper == type_tag_.upper;
872872
}
873873

874874
private:
875875
v8impl::Persistent<v8::Value> persistent_;
876876
void* data_;
877-
const napi_type_tag* type_tag_ = nullptr;
877+
napi_type_tag type_tag_;
878+
bool has_tag_ = false;
878879
};
879880

880881
} // end of namespace v8impl

test/js-native-api/test_object/test_object.c

+26-3
Original file line numberDiff line numberDiff line change
@@ -629,12 +629,29 @@ TypeTaggedInstance(napi_env env, napi_callback_info info) {
629629
size_t argc = 1;
630630
uint32_t type_index;
631631
napi_value instance, which_type;
632+
napi_type_tag tag;
633+
634+
// Below we copy the tag before setting it to prevent bugs where a pointer
635+
// to the tag (instead of the 128-bit tag value) is stored.
632636

633637
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &which_type, NULL, NULL));
634638
NODE_API_CALL(env, napi_get_value_uint32(env, which_type, &type_index));
635639
VALIDATE_TYPE_INDEX(env, type_index);
636640
NODE_API_CALL(env, napi_create_object(env, &instance));
637-
NODE_API_CALL(env, napi_type_tag_object(env, instance, &type_tags[type_index]));
641+
tag = type_tags[type_index];
642+
NODE_API_CALL(env, napi_type_tag_object(env, instance, &tag));
643+
644+
// Since the tag passed to napi_type_tag_object() was copied to the stack,
645+
// a type tagging implementation that uses a pointer instead of the
646+
// tag value would end up pointing to stack memory.
647+
// When CheckTypeTag() is called later on, it might be the case that this
648+
// stack address has been left untouched by accident (if no subsequent
649+
// function call has clobbered it), which means the pointer would still
650+
// point to valid data.
651+
// To make sure that tags are stored by value and not by reference,
652+
// clear this copy; any implementation using a pointer would end up with
653+
// random stack data or { 0, 0 }, but not the original tag value, and fail.
654+
memset(&tag, 0, sizeof(tag));
638655

639656
return instance;
640657
}
@@ -655,15 +672,21 @@ static napi_value TypeTaggedExternal(napi_env env, napi_callback_info info) {
655672
size_t argc = 1;
656673
uint32_t type_index;
657674
napi_value instance, which_type;
675+
napi_type_tag tag;
676+
677+
// See TypeTaggedInstance() for an explanation about why we copy the tag
678+
// to the stack and why we call memset on it after the external is tagged.
658679

659680
NODE_API_CALL(env,
660681
napi_get_cb_info(env, info, &argc, &which_type, NULL, NULL));
661682
NODE_API_CALL(env, napi_get_value_uint32(env, which_type, &type_index));
662683
VALIDATE_TYPE_INDEX(env, type_index);
663684
NODE_API_CALL(
664685
env, napi_create_external(env, IN_LIEU_OF_NULL, NULL, NULL, &instance));
665-
NODE_API_CALL(env,
666-
napi_type_tag_object(env, instance, &type_tags[type_index]));
686+
tag = type_tags[type_index];
687+
NODE_API_CALL(env, napi_type_tag_object(env, instance, &tag));
688+
689+
memset(&tag, 0, sizeof(tag));
667690

668691
return instance;
669692
}

0 commit comments

Comments
 (0)