Skip to content

Conversation

@cheesycod
Copy link

@cheesycod cheesycod commented Sep 24, 2025

This PR adds support for the v8 sandbox to rusty v8 (which is something critical to my use case of running untrusted user code). Sandbox is enabled along with pointer compression via v8_enable_pointer_compression. Note that due to limitations of the sandbox, enabling sandbox will cause the following API-wide changes:

  • (Shared)ArrayBuffer::new_backing_store_from_vec/boxed_slice is intentionally gated out as v8 sandbox requires all backing stores needing to be allocated within isolate sandbox. (Shared)ArrayBuffer::new_backing_store_from_ptr can be used in the event that a user does have data meeting this constraint
  • new_rust_allocator will not work as a consequence of the above. Backing stores are now fully v8 managed and rust managed ones can't (at least I haven't found a public API yet) allocate their own data within the sandbox.
  • The pointer to v8::External will now required to be a real heap-allocated pointer (this was always the case really, you should not be casting random numbers and passing to v8::External::new anyways). This is because v8's EPT needs to store the external tag which doesn't seem to work if the pointer isnt heap allocated when v8 sandbox is enabled

Copy link
Member

@ry ry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi - thanks for the patch and sorry for the slow review!

This change looks good, but CARGO_FEATURE_V8_ENABLE_SANDBOX and CARGO_FEATURE_V8_ENABLE_POINTER_COMPRESSION should be mentioned in the README with some description of what it does. Should mention that this is only supported when V8_FROM_SOURCE=1 is used and that these configurations aren't tested in our CI.

@cheesycod
Copy link
Author

Hi - thanks for the patch and sorry for the slow review!

This change looks good, but CARGO_FEATURE_V8_ENABLE_SANDBOX and CARGO_FEATURE_V8_ENABLE_POINTER_COMPRESSION should be mentioned in the README with some description of what it does. Should mention that this is only supported when V8_FROM_SOURCE=1 is used and that these configurations aren't tested in our CI.

Regarding CARGO_FEATURE_V8_ENABLE_SANDBOX and CARGO_FEATURE_V8_ENABLE_POINTER_COMPRESSION, those are just the environment variables cargo sets when executing the build.rs script to denote which features are enabled. I have pushed a commit to update the readme though with documentation for sandbox under a new "Experimental Features" section (since it is technically experimental)

@devsnek
Copy link
Member

devsnek commented Oct 10, 2025

I think it would be preferable to simply expose the api for creating a v8-allocated backing store, and letting application code perform the memcpy. Diverging from v8's exposed api surface tends to create more trouble down the line.

@cheesycod
Copy link
Author

cheesycod commented Oct 10, 2025

I think it would be preferable to simply expose the api for creating a v8-allocated backing store, and letting application code perform the memcpy. Diverging from v8's exposed api surface tends to create more trouble down the line.

So having a new_empty_backing_store function and then having the application code do a slice::copy_from_slice to copy the bytes (or having a sep helper to copy the bytes?)

@devsnek
Copy link
Member

devsnek commented Oct 10, 2025

as it happens, isn't it already exposed? https://docs.rs/v8/latest/v8/struct.ArrayBuffer.html#method.new_backing_store

and then having the application code do a slice::copy_from_slice

yes, you can build a slice from the existing methods on BackingStore

@cheesycod
Copy link
Author

cheesycod commented Oct 10, 2025

as it happens, isn't it already exposed? https://docs.rs/v8/latest/v8/struct.ArrayBuffer.html#method.new_backing_store

and then having the application code do a slice::copy_from_slice

yes, you can build a slice from the existing methods on BackingStore

Oh yeah, true. I guess I was more aiming for backwards compatibility here. Should I just flag the other backing store methods which take in data as not(feature = v8_enable_sandbox) then and remove the *_data_sandboxed from bindings.cc or is there smth else i should do

also, should rusty v8 have some rust helper method to do the work of copying a byte slice to a backingstore as it seems like a pretty fundamental operation

@devsnek
Copy link
Member

devsnek commented Oct 10, 2025

Oh yeah, true. I guess I was more aiming for backwards compatibility here. Should I just flag the other backing store methods which take in data as not(feature = v8_enable_sandbox) then and remove the *_data_sandboxed from bindings.cc or is there smth else i should do

The exact requirement is here: https://source.chromium.org/chromium/chromium/src/+/main:v8/src/api/api.cc;l=8956-8960;drc=62a9a00176f862e688fa47919ed6f19b59f77b5f;bpv=0;bpt=1

As it seems the APIs which allocate a BackingStore from an existing pointer still function when the sandbox is enabled, I wouldn't gate them out. Maybe adding the additional sandbox requirements to the doc comment could be useful though.

also, should rusty v8 have some rust helper method to do the work of copying a byte slice to a backingstore as it seems like a pretty fundamental operation

I'd be against it I think. The exact way in which you mutate a backing store is highly dependent on how you manage mutable aliasing in your application code.

@cheesycod
Copy link
Author

cheesycod commented Oct 10, 2025

The exact requirement is here: https://source.chromium.org/chromium/chromium/src/+/main:v8/src/api/api.cc;l=8956-8960;drc=62a9a00176f862e688fa47919ed6f19b59f77b5f;bpv=0;bpt=1

As it seems the APIs which allocate a BackingStore from an existing pointer still function when the sandbox is enabled, I wouldn't gate them out. Maybe adding the additional sandbox requirements to the doc comment could be useful though.

Yeah, the API still works (kinda), I'm just not sure how to actually ever use this API in sandbox mode with rusty_v8's current API without crashing which is why I gated it for now (unless there's something I'm missing, there's no easy way to just allocate the data within the sandbox address space in rusty_v8 right now). It also feels icky in the sense that most user code which uses BackingStores will probably break without even a compiler error if they enable sandbox mode.

Would it be acceptable to just gate the helper methods (new_backing_store_from_boxed_slice, new_backing_store_from_vec and new_backing_store_from_bytes) but keep the from_ptr method to ensure most users don't encounter crashes relating to backing stores when enabling sandbox mode etc.

@devsnek
Copy link
Member

devsnek commented Oct 10, 2025

That sounds alright.

@cheesycod
Copy link
Author

That sounds alright.

Have made a commit which does this

@cheesycod
Copy link
Author

Any update?

@ry
Copy link
Member

ry commented Oct 23, 2025

@cheesycod Happy to land this if you can rebase and fix up the latest review comments. (Note we've just upgraded main to 14.2)

@cheesycod
Copy link
Author

@cheesycod Happy to land this if you can rebase and fix up the latest review comments. (Note we've just upgraded main to 14.2)

Oh yeah, sorry about the long delay, I was super busy this week. Will resolve conflicts and fix up the review comments ASAP

@cheesycod
Copy link
Author

@ry @devsnek BTW, while testing on v14.2, I found that concurrent isolate uses single threaded platform for some reason but then spins up threads and spawns isolates in them (which doesnt seem to work with sandbox). Is it ok if i update the test to use a new_default_platform if using v8 sandbox mode?

@cheesycod
Copy link
Author

@devsnek btw, waiting for you to look over my code again. I think I resolved the feedback you gave

@cheesycod
Copy link
Author

@cheesycod Happy to land this if you can rebase and fix up the latest review comments. (Note we've just upgraded main to 14.2)

An update on this, I fixed the review comments to the best of my ability as well as rebased. Just waiting on further feedback/review

let len = v.len();
let store = v8::ArrayBuffer::new_backing_store_from_vec(v).make_shared();
let buf = v8::ArrayBuffer::with_backing_store(&scope, &store);
assert_eq!(buf.byte_length(), len);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this stuff removed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, another leftover from the previous sandbox impl. Will fix

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants