Skip to content

Commit 147fc50

Browse files
committed
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]>
1 parent e02087c commit 147fc50

1 file changed

Lines changed: 31 additions & 36 deletions

File tree

src/ffi/data.cc

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -675,26 +675,17 @@ void ExportBytes(const FunctionCallbackInfo<Value>& args) {
675675
return;
676676
}
677677

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

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]);
682+
if (args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer() ||
683+
args[0]->IsArrayBufferView()) {
684+
view.ReadValue(args[0]);
692685
if (view.WasDetached()) {
693686
THROW_ERR_INVALID_ARG_VALUE(env, "Invalid ArrayBufferView backing store");
694687
return;
695688
}
696-
source_data = view.data();
697-
source_len = view.length();
698689
} else {
699690
THROW_ERR_INVALID_ARG_TYPE(
700691
env,
@@ -713,12 +704,12 @@ void ExportBytes(const FunctionCallbackInfo<Value>& args) {
713704
return;
714705
}
715706

716-
if (len < source_len) {
707+
if (len < view.length()) {
717708
THROW_ERR_OUT_OF_RANGE(env, "The length must be >= source byte length");
718709
return;
719710
}
720711

721-
if (ptr == 0 && source_len > 0) {
712+
if (ptr == 0 && view.length() > 0) {
722713
THROW_ERR_FFI_INVALID_POINTER(env,
723714
"Cannot create a buffer from a null pointer");
724715
return;
@@ -733,9 +724,7 @@ void ExportBytes(const FunctionCallbackInfo<Value>& args) {
733724
return;
734725
}
735726

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

741730
void GetRawPointer(const FunctionCallbackInfo<Value>& args) {
@@ -752,28 +741,34 @@ void GetRawPointer(const FunctionCallbackInfo<Value>& args) {
752741
}
753742

754743
uintptr_t ptr = 0;
744+
size_t offset = 0;
745+
std::shared_ptr<BackingStore> store;
755746

756747
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());
748+
store = args[0].As<ArrayBuffer>()->GetBackingStore();
749+
} else if (args[0]->IsSharedArrayBuffer()) {
750+
store = args[0].As<SharedArrayBuffer>()->GetBackingStore();
764751
} 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());
752+
// Access the store here to ensure that it exists. Small typed arrays
753+
// may not have a store until this point and can instead be stored
754+
// entirely in-heap.
755+
store = args[0].As<ArrayBufferView>()->Buffer()->GetBackingStore();
756+
offset = args[0].As<ArrayBufferView>()->ByteOffset();
771757
} else {
772-
THROW_ERR_INVALID_ARG_TYPE(
773-
env,
774-
"The first argument must be a Buffer, ArrayBuffer, or ArrayBufferView");
758+
THROW_ERR_INVALID_ARG_TYPE(env,
759+
"The first argument must be a Buffer, "
760+
"ArrayBuffer, or ArrayBufferView");
761+
return;
762+
}
763+
764+
// WARNING: There is no inherent guarantee that the pointer returned
765+
// from this function will be valid beyond the lifetime of the BackingStore
766+
// instance!
767+
if (!store) {
768+
THROW_ERR_INVALID_ARG_VALUE(env, "Invalid ArrayBuffer backing store");
775769
return;
776770
}
771+
ptr = reinterpret_cast<uintptr_t>(store->Data()) + offset;
777772

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

0 commit comments

Comments
 (0)