-
Notifications
You must be signed in to change notification settings - Fork 334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Erikcorry/multi sandbox #3102
base: main
Are you sure you want to change the base?
Erikcorry/multi sandbox #3102
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,19 +69,29 @@ using BufferSourceViewConstructor = v8::Local<v8::Value> (*)(Lock&, BackingStore | |
// the byte length, offset, element size, and constructor type allowing the view to be | ||
// recreated. | ||
// | ||
// The BackingStore can be safely used outside of the isolate lock and can even be passed | ||
// into another isolate if necessary. | ||
// The BackingStore must be created in a particular isolate, but it can be | ||
// safely used outside of the isolate lock. It cannot be passed to a different | ||
// isolate, unless they are in the same isolate group, which means they are in | ||
// the same sandbox. | ||
class BackingStore { | ||
public: | ||
template <BufferSourceType T = v8::Uint8Array> | ||
static BackingStore from(kj::Array<kj::byte> data) { | ||
static BackingStore from(Lock& js, kj::Array<kj::byte> data) { | ||
// Creates a new BackingStore that takes over ownership of the given kj::Array. | ||
// The bytes may be moved if they are not inside the sandbox already. | ||
size_t size = data.size(); | ||
auto ptr = new kj::Array<byte>(kj::mv(data)); | ||
return BackingStore( | ||
v8::ArrayBuffer::NewBackingStore(ptr->begin(), size, | ||
[](void*, size_t, void* ptr) { delete reinterpret_cast<kj::Array<byte>*>(ptr); }, ptr), | ||
size, 0, getBufferSourceElementSize<T>(), construct<T>, checkIsIntegerType<T>()); | ||
if (js.v8Isolate->GetGroup().SandboxContains(data.begin())) { | ||
auto ptr = new kj::Array<byte>(kj::mv(data)); | ||
return BackingStore( | ||
v8::ArrayBuffer::NewBackingStore(ptr->begin(), size, | ||
[](void*, size_t, void* ptr) { delete reinterpret_cast<kj::Array<byte>*>(ptr); }, ptr), | ||
size, 0, getBufferSourceElementSize<T>(), construct<T>, checkIsIntegerType<T>()); | ||
} else { | ||
auto result = BackingStore(v8::ArrayBuffer::NewBackingStore(js.v8Isolate, size), size, 0, | ||
getBufferSourceElementSize<T>(), construct<T>, checkIsIntegerType<T>()); | ||
memcpy(result.asArrayPtr().begin(), data.begin(), size); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, using |
||
return result; | ||
} | ||
} | ||
|
||
// Creates a new BackingStore of the given size. | ||
|
@@ -449,7 +459,7 @@ class BufferSourceWrapper { | |
}; | ||
|
||
inline BufferSource Lock::arrayBuffer(kj::Array<kj::byte> data) { | ||
return BufferSource(*this, BackingStore::from<v8::ArrayBuffer>(kj::mv(data))); | ||
return BufferSource(*this, BackingStore::from<v8::ArrayBuffer>(*this, kj::mv(data))); | ||
} | ||
|
||
} // namespace workerd::jsg |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -916,28 +916,40 @@ class ArrayBufferWrapper { | |
// v8::ArrayBuffer::NewBackingStore() that accepts a deleter callback, and arrange for it to | ||
// delete an Array<byte> placed on the heap. | ||
// | ||
// TODO(perf): We could avoid an allocation here, perhaps, by decomposing the kj::Array<byte> | ||
// into its component pointer and disposer, and then pass the disposer pointer as the | ||
// "deleter_data" for NewBackingStore. However, KJ doesn't give us any way to decompose an | ||
// Array<T> this way, and it might not want to, as this could make it impossible to support | ||
// unifying Array<T> and Vector<T> in the future (i.e. making all Array<T>s growable). So | ||
// it may be best to stick with allocating an Array<byte> on the heap after all... | ||
size_t size = value.size(); | ||
if (size == 0) { | ||
// BackingStore doesn't call custom deleter if begin is null, which it often is for empty | ||
// arrays. | ||
return v8::ArrayBuffer::New(isolate, 0); | ||
} | ||
byte* begin = value.begin(); | ||
if (isolate->GetGroup().SandboxContains(begin)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps this could be extracted into a separate utility since there are two places doing this same check and operation? |
||
// TODO(perf): We could avoid an allocation here, perhaps, by decomposing the | ||
// kj::Array<byte> into its component pointer and disposer, and then pass the disposer | ||
// pointer as the "deleter_data" for NewBackingStore. However, KJ doesn't give us any way | ||
// to decompose an Array<T> this way, and it might not want to, as this could make it | ||
// impossible to support unifying Array<T> and Vector<T> in the future (i.e. making all | ||
// Array<T>s growable). So it may be best to stick with allocating an Array<byte> on the | ||
// heap after all... | ||
auto ownerPtr = new kj::Array<byte>(kj::mv(value)); | ||
|
||
std::unique_ptr<v8::BackingStore> backing = | ||
v8::ArrayBuffer::NewBackingStore(begin, size, [](void* begin, size_t size, void* ownerPtr) { | ||
delete reinterpret_cast<kj::Array<byte>*>(ownerPtr); | ||
}, ownerPtr); | ||
|
||
return v8::ArrayBuffer::New(isolate, kj::mv(backing)); | ||
} else { | ||
// The Array is not already inside the sandbox. We have to make a copy and move it in. | ||
// For performance reasons we might want to throw here and fix all callers to allocate | ||
// inside the sandbox. | ||
std::unique_ptr<v8::BackingStore> in_sandbox = | ||
v8::ArrayBuffer::NewBackingStore(isolate, size, v8::BackingStoreInitializationMode::kUninitialized); | ||
|
||
auto ownerPtr = new kj::Array<byte>(kj::mv(value)); | ||
|
||
std::unique_ptr<v8::BackingStore> backing = | ||
v8::ArrayBuffer::NewBackingStore(begin, size, [](void* begin, size_t size, void* ownerPtr) { | ||
delete reinterpret_cast<kj::Array<byte>*>(ownerPtr); | ||
}, ownerPtr); | ||
memcpy(in_sandbox->Data(), value.begin(), size); | ||
|
||
return v8::ArrayBuffer::New(isolate, kj::mv(backing)); | ||
return v8::ArrayBuffer::New(isolate, kj::mv(in_sandbox)); | ||
} | ||
} | ||
|
||
v8::Local<v8::ArrayBuffer> wrap(v8::Local<v8::Context> context, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.