Skip to content

Commit c69c6f1

Browse files
authored
src: simplify and fix FFI ArrayBuffer accesses
`ArrayBufferViewContents` supports `ArrayBuffer`s, so we do not need to provide special treatment for those. Also, small typed arrays may not have backing stores until their corresponding `ArrayBuffer`s are accessed, so do that in order to avoid accidentally accessing arbitrary stack memory. Signed-off-by: Anna Henningsen <[email protected]> PR-URL: #62857 Reviewed-By: Paolo Insogna <[email protected]> Reviewed-By: Daeyeon Jeong <[email protected]>
1 parent 5be0bd9 commit c69c6f1

1 file changed

Lines changed: 32 additions & 36 deletions

File tree

src/ffi/data.cc

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ using v8::MaybeLocal;
2525
using v8::NewStringType;
2626
using v8::Number;
2727
using v8::Object;
28+
using v8::SharedArrayBuffer;
2829
using v8::String;
2930
using v8::Value;
3031

@@ -675,26 +676,17 @@ void ExportBytes(const FunctionCallbackInfo<Value>& args) {
675676
return;
676677
}
677678

678-
const uint8_t* source_data = nullptr;
679-
size_t source_len = 0;
679+
// This needs to be kept alive until the data
680+
// is actually copied.
681+
ArrayBufferViewContents<uint8_t> view;
680682

681-
if (args[0]->IsArrayBuffer()) {
682-
Local<ArrayBuffer> array_buffer = args[0].As<ArrayBuffer>();
683-
std::shared_ptr<BackingStore> store = array_buffer->GetBackingStore();
684-
if (!store) {
685-
THROW_ERR_INVALID_ARG_VALUE(env, "Invalid ArrayBuffer backing store");
686-
return;
687-
}
688-
source_data = static_cast<const uint8_t*>(store->Data());
689-
source_len = array_buffer->ByteLength();
690-
} else if (args[0]->IsArrayBufferView()) {
691-
ArrayBufferViewContents<uint8_t> view(args[0]);
683+
if (args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer() ||
684+
args[0]->IsArrayBufferView()) {
685+
view.ReadValue(args[0]);
692686
if (view.WasDetached()) {
693687
THROW_ERR_INVALID_ARG_VALUE(env, "Invalid ArrayBufferView backing store");
694688
return;
695689
}
696-
source_data = view.data();
697-
source_len = view.length();
698690
} else {
699691
THROW_ERR_INVALID_ARG_TYPE(
700692
env,
@@ -713,12 +705,12 @@ void ExportBytes(const FunctionCallbackInfo<Value>& args) {
713705
return;
714706
}
715707

716-
if (len < source_len) {
708+
if (len < view.length()) {
717709
THROW_ERR_OUT_OF_RANGE(env, "The length must be >= source byte length");
718710
return;
719711
}
720712

721-
if (ptr == 0 && source_len > 0) {
713+
if (ptr == 0 && view.length() > 0) {
722714
THROW_ERR_FFI_INVALID_POINTER(env,
723715
"Cannot create a buffer from a null pointer");
724716
return;
@@ -733,9 +725,7 @@ void ExportBytes(const FunctionCallbackInfo<Value>& args) {
733725
return;
734726
}
735727

736-
if (source_len > 0) {
737-
std::memcpy(reinterpret_cast<void*>(ptr), source_data, source_len);
738-
}
728+
std::memcpy(reinterpret_cast<void*>(ptr), view.data(), view.length());
739729
}
740730

741731
void GetRawPointer(const FunctionCallbackInfo<Value>& args) {
@@ -752,28 +742,34 @@ void GetRawPointer(const FunctionCallbackInfo<Value>& args) {
752742
}
753743

754744
uintptr_t ptr = 0;
745+
size_t offset = 0;
746+
std::shared_ptr<BackingStore> store;
755747

756748
if (args[0]->IsArrayBuffer()) {
757-
Local<ArrayBuffer> array_buffer = args[0].As<ArrayBuffer>();
758-
std::shared_ptr<BackingStore> store = array_buffer->GetBackingStore();
759-
if (!store) {
760-
THROW_ERR_INVALID_ARG_VALUE(env, "Invalid ArrayBuffer backing store");
761-
return;
762-
}
763-
ptr = reinterpret_cast<uintptr_t>(store->Data());
749+
store = args[0].As<ArrayBuffer>()->GetBackingStore();
750+
} else if (args[0]->IsSharedArrayBuffer()) {
751+
store = args[0].As<SharedArrayBuffer>()->GetBackingStore();
764752
} else if (args[0]->IsArrayBufferView()) {
765-
ArrayBufferViewContents<uint8_t> view(args[0]);
766-
if (view.WasDetached()) {
767-
THROW_ERR_INVALID_ARG_VALUE(env, "Invalid ArrayBufferView backing store");
768-
return;
769-
}
770-
ptr = reinterpret_cast<uintptr_t>(view.data());
753+
// Access the store here to ensure that it exists. Small typed arrays
754+
// may not have a store until this point and can instead be stored
755+
// entirely in-heap.
756+
store = args[0].As<ArrayBufferView>()->Buffer()->GetBackingStore();
757+
offset = args[0].As<ArrayBufferView>()->ByteOffset();
771758
} else {
772-
THROW_ERR_INVALID_ARG_TYPE(
773-
env,
774-
"The first argument must be a Buffer, ArrayBuffer, or ArrayBufferView");
759+
THROW_ERR_INVALID_ARG_TYPE(env,
760+
"The first argument must be a Buffer, "
761+
"ArrayBuffer, or ArrayBufferView");
762+
return;
763+
}
764+
765+
// WARNING: There is no inherent guarantee that the pointer returned
766+
// from this function will be valid beyond the lifetime of the BackingStore
767+
// instance!
768+
if (!store) {
769+
THROW_ERR_INVALID_ARG_VALUE(env, "Invalid ArrayBuffer backing store");
775770
return;
776771
}
772+
ptr = reinterpret_cast<uintptr_t>(store->Data()) + offset;
777773

778774
args.GetReturnValue().Set(
779775
BigInt::NewFromUnsigned(isolate, static_cast<uint64_t>(ptr)));

0 commit comments

Comments
 (0)