Blog Sandbox
After almost three years since the initial design document and hundreds of CLs in the meantime, the V8 Sandbox — a lightweight, in-process sandbox for V8 — has now progressed to the point where it is no longer considered an experimental security feature. Starting today, the V8 Sandbox is included in Chrome's Vulnerability Reward Program (VRP). While there are still a number of issues to resolve before it becomes a strong security boundary, the VRP inclusion is an important step in that direction. Chrome 123 could therefore be considered to be a sort of "beta" release for the sandbox. This blog post uses this opportunity to the motivation behind the sandbox, show how it prevents memory corruption in V8 from spreading within the host process, and ultimately explain why it is a necessary step towards memory safety. Motivation # Memory safety remains a relevant problem: all Chrome exploits caught in the wild in the last three years (2021 – 2023) started out with a memory corruption vulnerability in a Chrome renderer process that was exploited for remote code execution (RCE). Of these, 60% were vulnerabilities in V8. However, there is a catch: V8 vulnerabilities are rarely "classic" memory corruption bugs (use-after-frees, out-of-bounds accesses, etc.) but instead subtle logic issues which can in turn be exploited to corrupt memory. As such, existing memory safety solutions are, for the most part, not applicable to V8. In particular, neither switching to a memory safe language , such as Rust, nor using current or future hardware memory safety features, such as memory tagging , can help with the security challenges faced by V8 today. To understand why, consider a highly simplified, hypothetical JavaScript engine vulnerability: the implementation of JSArray::fizzbuzz() , which replaces values in the array that are divisible by 3 with "fizz", divisible by 5 with "buzz", and divisible by both 3 and 5 with "fizzbuzz". Below is an implementation of that function in C++. JSArray::buffer_ can be thought of as a JSValue* , that is, a pointer to an array of JavaScript values, and JSArray::length_ contains the current size of that buffer. 1. ( index = 0 ; index JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i JSObject :: GetPropertyNames ( ) { num_properties = TotalNumberOfProperties ( ) ; std :: vector properties ( num_properties ) ; ( i = 0 ; i < NumberOfInObjectProperties ( ) ; i ++ ) { properties [ i ] = GetNameOfInObjectProperty ( i ) ; } This code makes the (reasonable) assumption that the number of properties stored directly in a JSObject must be less than the total number of properties of that object. However, assuming these numbers are simply stored as integers somewhere in the JSObject, an attacker could corrupt one of them to break this invariant. Subsequently, the access into the (out-of-sandbox) std::vector would go out of bounds. Adding an explicit bounds check, for example with an SBXCHECK , would fix this. Encouragingly, nearly all "sandbox violations" discovered so far are like this: trivial (1st order) memory corruption bugs such as use-after-frees or out-of-bounds accesses due to lack of a bounds check. Contrary to the 2nd order vulnerabilities typically found in V8, these sandbox bugs could actually be prevented or mitigated by the approaches discussed earlier. In fact, the particular bug above would already be mitigated today due to Chrome's libc++ hardening . As such, the hope is that in the long run, the sandbox becomes a more defensible security boundary than V8 itself. While the currently available data set of sandbox bugs is very limited, the VRP integration launching today will hopefully help produce a clearer picture of the type of vulnerabilities encountered on the sandbox attack surface. Performance # One major advantage of this approach is that it is fundamentally cheap: the overhead caused by the sandbox comes mostly from the pointer table indirection for external objects (costing roughly one additional memory load) and to a lesser extent from the use of offsets instead of raw pointers (costing mostly just a shift+add operation, which is very cheap). The current overhead of the sandbox is therefore only around 1% or less on typical workloads (measured using the Speedometer and JetStream benchmark suites). This allows the V8 Sandbox to be enabled by default on compatible platforms. Testing # A desirable feature for any security boundary is testability: the ability to manually and automatically test that the promised security guarantees actually hold in practice. This requires a clear attacker model, a way to "emulate" an attacker, and ideally a way of automatically determining when the security boundary has failed. The V8 Sandbox fulfills all of these requirements: A clear attacker model: it is assumed that an attacker can read and write arbitrarily inside the V8 Sandbox. The goal is to prevent memory corruption outside of the sandbox. A way to emulate an attacker: V8 provides a "memory corruption API" when built with the v8_enable_memory_corruption_api = true flag. This emulates the primitives obtained from typical V8 vulnerabilities and in particular provides full read- and write access inside the sandbox. A way to detect "sandbox violations": V8 provides a "sandbox testing" mode (enabled via either --sandbox-testing or --sandbox-fuzzing ) which installs a signal handler that determines if a signal such as SIGSEGV represents a violation of the sandbox's security guarantees. Ultimately, this allows the sandbox to be integrated into Chrome's VRP program and be fuzzed by specialized fuzzers. Usage # The V8 Sandbox must be enabled/disabled at build time using the v8_enable_sandbox build flag. It is (for technical reasons) not possible to enable/disable the sandbox at runtime. The V8 Sandbox requires a 64-bit system as it needs to reserve a large amount of virtual address space, currently one terabyte. The V8 Sandbox has already been enabled by default on 64-bit (specifically x64 and arm64) versions of Chrome on Android, ChromeOS, Linux, macOS, and Windows for roughly the last two years. Even though the sandbox was (and still is) not feature complete, this was mainly done to ensure that it does not cause stability issues and to collect real-world performance statistics. Consequently, recent V8 exploits already had to work their way past the sandbox, providing helpful early feedback on its security properties. Conclusion # The V8 Sandbox is a new security mechanism designed to prevent memory corruption in V8 from impacting other memory in the process. The sandbox is motivated by the fact that current memory safety technologies are largely inapplicable to optimizing JavaScript engines. While these technologies fail to prevent memory corruption in V8 itself, they can in fact protect the V8 Sandbox attack surface. The sandbox is therefore a necessary step towards memory safety.
This code makes the (reasonable) assumption that the number of properties stored directly in a JSObject must be less than the total number of properties of that object. However, assuming these numbers are simply stored as integers somewhere in the JSObject, an attacker could corrupt one of them to break this invariant. Subsequently, the access into the (out-of-sandbox) std::vector would go out of bounds. Adding an explicit bounds check, for example with an SBXCHECK , would fix this. Encouragingly, nearly all "sandbox violations" discovered so far are like this: trivial (1st order) memory corruption bugs such as use-after-frees or out-of-bounds accesses due to lack of a bounds check. Contrary to the 2nd order vulnerabilities typically found in V8, these sandbox bugs could actually be prevented or mitigated by the approaches discussed earlier. In fact, the particular bug above would already be mitigated today due to Chrome's libc++ hardening . As such, the hope is that in the long run, the sandbox becomes a more defensible security boundary than V8 itself. While the currently available data set of sandbox bugs is very limited, the VRP integration launching today will hopefully help produce a clearer picture of the type of vulnerabilities encountered on the sandbox attack surface. Performance # One major advantage of this approach is that it is fundamentally cheap: the overhead caused by the sandbox comes mostly from the pointer table indirection for external objects (costing roughly one additional memory load) and to a lesser extent from the use of offsets instead of raw pointers (costing mostly just a shift+add operation, which is very cheap). The current overhead of the sandbox is therefore only around 1% or less on typical workloads (measured using the Speedometer and JetStream benchmark suites). This allows the V8 Sandbox to be enabled by default on compatible platforms. Testing # A desirable feature for any security boundary is testability: the ability to manually and automatically test that the promised security guarantees actually hold in practice. This requires a clear attacker model, a way to "emulate" an attacker, and ideally a way of automatically determining when the security boundary has failed. The V8 Sandbox fulfills all of these requirements: A clear attacker model: it is assumed that an attacker can read and write arbitrarily inside the V8 Sandbox. The goal is to prevent memory corruption outside of the sandbox. A way to emulate an attacker: V8 provides a "memory corruption API" when built with the v8_enable_memory_corruption_api = true flag. This emulates the primitives obtained from typical V8 vulnerabilities and in particular provides full read- and write access inside the sandbox. A way to detect "sandbox violations": V8 provides a "sandbox testing" mode (enabled via either --sandbox-testing or --sandbox-fuzzing ) which installs a signal handler that determines if a signal such as SIGSEGV represents a violation of the sandbox's security guarantees. Ultimately, this allows the sandbox to be integrated into Chrome's VRP program and be fuzzed by specialized fuzzers. Usage # The V8 Sandbox must be enabled/disabled at build time using the v8_enable_sandbox build flag. It is (for technical reasons) not possible to enable/disable the sandbox at runtime. The V8 Sandbox requires a 64-bit system as it needs to reserve a large amount of virtual address space, currently one terabyte. The V8 Sandbox has already been enabled by default on 64-bit (specifically x64 and arm64) versions of Chrome on Android, ChromeOS, Linux, macOS, and Windows for roughly the last two years. Even though the sandbox was (and still is) not feature complete, this was mainly done to ensure that it does not cause stability issues and to collect real-world performance statistics. Consequently, recent V8 exploits already had to work their way past the sandbox, providing helpful early feedback on its security properties. Conclusion # The V8 Sandbox is a new security mechanism designed to prevent memory corruption in V8 from impacting other memory in the process. The sandbox is motivated by the fact that current memory safety technologies are largely inapplicable to optimizing JavaScript engines. While these technologies fail to prevent memory corruption in V8 itself, they can in fact protect the V8 Sandbox attack surface. The sandbox is therefore a necessary step towards memory safety.
Encouragingly, nearly all "sandbox violations" discovered so far are like this: trivial (1st order) memory corruption bugs such as use-after-frees or out-of-bounds accesses due to lack of a bounds check. Contrary to the 2nd order vulnerabilities typically found in V8, these sandbox bugs could actually be prevented or mitigated by the approaches discussed earlier. In fact, the particular bug above would already be mitigated today due to Chrome's libc++ hardening . As such, the hope is that in the long run, the sandbox becomes a more defensible security boundary than V8 itself. While the currently available data set of sandbox bugs is very limited, the VRP integration launching today will hopefully help produce a clearer picture of the type of vulnerabilities encountered on the sandbox attack surface. Performance # One major advantage of this approach is that it is fundamentally cheap: the overhead caused by the sandbox comes mostly from the pointer table indirection for external objects (costing roughly one additional memory load) and to a lesser extent from the use of offsets instead of raw pointers (costing mostly just a shift+add operation, which is very cheap). The current overhead of the sandbox is therefore only around 1% or less on typical workloads (measured using the Speedometer and JetStream benchmark suites). This allows the V8 Sandbox to be enabled by default on compatible platforms. Testing # A desirable feature for any security boundary is testability: the ability to manually and automatically test that the promised security guarantees actually hold in practice. This requires a clear attacker model, a way to "emulate" an attacker, and ideally a way of automatically determining when the security boundary has failed. The V8 Sandbox fulfills all of these requirements: A clear attacker model: it is assumed that an attacker can read and write arbitrarily inside the V8 Sandbox. The goal is to prevent memory corruption outside of the sandbox. A way to emulate an attacker: V8 provides a "memory corruption API" when built with the v8_enable_memory_corruption_api = true flag. This emulates the primitives obtained from typical V8 vulnerabilities and in particular provides full read- and write access inside the sandbox. A way to detect "sandbox violations": V8 provides a "sandbox testing" mode (enabled via either --sandbox-testing or --sandbox-fuzzing ) which installs a signal handler that determines if a signal such as SIGSEGV represents a violation of the sandbox's security guarantees. Ultimately, this allows the sandbox to be integrated into Chrome's VRP program and be fuzzed by specialized fuzzers. Usage # The V8 Sandbox must be enabled/disabled at build time using the v8_enable_sandbox build flag. It is (for technical reasons) not possible to enable/disable the sandbox at runtime. The V8 Sandbox requires a 64-bit system as it needs to reserve a large amount of virtual address space, currently one terabyte. The V8 Sandbox has already been enabled by default on 64-bit (specifically x64 and arm64) versions of Chrome on Android, ChromeOS, Linux, macOS, and Windows for roughly the last two years. Even though the sandbox was (and still is) not feature complete, this was mainly done to ensure that it does not cause stability issues and to collect real-world performance statistics. Consequently, recent V8 exploits already had to work their way past the sandbox, providing helpful early feedback on its security properties. Conclusion # The V8 Sandbox is a new security mechanism designed to prevent memory corruption in V8 from impacting other memory in the process. The sandbox is motivated by the fact that current memory safety technologies are largely inapplicable to optimizing JavaScript engines. While these technologies fail to prevent memory corruption in V8 itself, they can in fact protect the V8 Sandbox attack surface. The sandbox is therefore a necessary step towards memory safety.
One major advantage of this approach is that it is fundamentally cheap: the overhead caused by the sandbox comes mostly from the pointer table indirection for external objects (costing roughly one additional memory load) and to a lesser extent from the use of offsets instead of raw pointers (costing mostly just a shift+add operation, which is very cheap). The current overhead of the sandbox is therefore only around 1% or less on typical workloads (measured using the Speedometer and JetStream benchmark suites). This allows the V8 Sandbox to be enabled by default on compatible platforms. Testing # A desirable feature for any security boundary is testability: the ability to manually and automatically test that the promised security guarantees actually hold in practice. This requires a clear attacker model, a way to "emulate" an attacker, and ideally a way of automatically determining when the security boundary has failed. The V8 Sandbox fulfills all of these requirements: A clear attacker model: it is assumed that an attacker can read and write arbitrarily inside the V8 Sandbox. The goal is to prevent memory corruption outside of the sandbox. A way to emulate an attacker: V8 provides a "memory corruption API" when built with the v8_enable_memory_corruption_api = true flag. This emulates the primitives obtained from typical V8 vulnerabilities and in particular provides full read- and write access inside the sandbox. A way to detect "sandbox violations": V8 provides a "sandbox testing" mode (enabled via either --sandbox-testing or --sandbox-fuzzing ) which installs a signal handler that determines if a signal such as SIGSEGV represents a violation of the sandbox's security guarantees. Ultimately, this allows the sandbox to be integrated into Chrome's VRP program and be fuzzed by specialized fuzzers. Usage # The V8 Sandbox must be enabled/disabled at build time using the v8_enable_sandbox build flag. It is (for technical reasons) not possible to enable/disable the sandbox at runtime. The V8 Sandbox requires a 64-bit system as it needs to reserve a large amount of virtual address space, currently one terabyte. The V8 Sandbox has already been enabled by default on 64-bit (specifically x64 and arm64) versions of Chrome on Android, ChromeOS, Linux, macOS, and Windows for roughly the last two years. Even though the sandbox was (and still is) not feature complete, this was mainly done to ensure that it does not cause stability issues and to collect real-world performance statistics. Consequently, recent V8 exploits already had to work their way past the sandbox, providing helpful early feedback on its security properties. Conclusion # The V8 Sandbox is a new security mechanism designed to prevent memory corruption in V8 from impacting other memory in the process. The sandbox is motivated by the fact that current memory safety technologies are largely inapplicable to optimizing JavaScript engines. While these technologies fail to prevent memory corruption in V8 itself, they can in fact protect the V8 Sandbox attack surface. The sandbox is therefore a necessary step towards memory safety.
A desirable feature for any security boundary is testability: the ability to manually and automatically test that the promised security guarantees actually hold in practice. This requires a clear attacker model, a way to "emulate" an attacker, and ideally a way of automatically determining when the security boundary has failed. The V8 Sandbox fulfills all of these requirements: A clear attacker model: it is assumed that an attacker can read and write arbitrarily inside the V8 Sandbox. The goal is to prevent memory corruption outside of the sandbox. A way to emulate an attacker: V8 provides a "memory corruption API" when built with the v8_enable_memory_corruption_api = true flag. This emulates the primitives obtained from typical V8 vulnerabilities and in particular provides full read- and write access inside the sandbox. A way to detect "sandbox violations": V8 provides a "sandbox testing" mode (enabled via either --sandbox-testing or --sandbox-fuzzing ) which installs a signal handler that determines if a signal such as SIGSEGV represents a violation of the sandbox's security guarantees. Ultimately, this allows the sandbox to be integrated into Chrome's VRP program and be fuzzed by specialized fuzzers. Usage # The V8 Sandbox must be enabled/disabled at build time using the v8_enable_sandbox build flag. It is (for technical reasons) not possible to enable/disable the sandbox at runtime. The V8 Sandbox requires a 64-bit system as it needs to reserve a large amount of virtual address space, currently one terabyte. The V8 Sandbox has already been enabled by default on 64-bit (specifically x64 and arm64) versions of Chrome on Android, ChromeOS, Linux, macOS, and Windows for roughly the last two years. Even though the sandbox was (and still is) not feature complete, this was mainly done to ensure that it does not cause stability issues and to collect real-world performance statistics. Consequently, recent V8 exploits already had to work their way past the sandbox, providing helpful early feedback on its security properties. Conclusion # The V8 Sandbox is a new security mechanism designed to prevent memory corruption in V8 from impacting other memory in the process. The sandbox is motivated by the fact that current memory safety technologies are largely inapplicable to optimizing JavaScript engines. While these technologies fail to prevent memory corruption in V8 itself, they can in fact protect the V8 Sandbox attack surface. The sandbox is therefore a necessary step towards memory safety.
A clear attacker model: it is assumed that an attacker can read and write arbitrarily inside the V8 Sandbox. The goal is to prevent memory corruption outside of the sandbox. A way to emulate an attacker: V8 provides a "memory corruption API" when built with the v8_enable_memory_corruption_api = true flag. This emulates the primitives obtained from typical V8 vulnerabilities and in particular provides full read- and write access inside the sandbox. A way to detect "sandbox violations": V8 provides a "sandbox testing" mode (enabled via either --sandbox-testing or --sandbox-fuzzing ) which installs a signal handler that determines if a signal such as SIGSEGV represents a violation of the sandbox's security guarantees.
A way to emulate an attacker: V8 provides a "memory corruption API" when built with the v8_enable_memory_corruption_api = true flag. This emulates the primitives obtained from typical V8 vulnerabilities and in particular provides full read- and write access inside the sandbox. A way to detect "sandbox violations": V8 provides a "sandbox testing" mode (enabled via either --sandbox-testing or --sandbox-fuzzing ) which installs a signal handler that determines if a signal such as SIGSEGV represents a violation of the sandbox's security guarantees.
A way to detect "sandbox violations": V8 provides a "sandbox testing" mode (enabled via either --sandbox-testing or --sandbox-fuzzing ) which installs a signal handler that determines if a signal such as SIGSEGV represents a violation of the sandbox's security guarantees.
Ultimately, this allows the sandbox to be integrated into Chrome's VRP program and be fuzzed by specialized fuzzers. Usage # The V8 Sandbox must be enabled/disabled at build time using the v8_enable_sandbox build flag. It is (for technical reasons) not possible to enable/disable the sandbox at runtime. The V8 Sandbox requires a 64-bit system as it needs to reserve a large amount of virtual address space, currently one terabyte. The V8 Sandbox has already been enabled by default on 64-bit (specifically x64 and arm64) versions of Chrome on Android, ChromeOS, Linux, macOS, and Windows for roughly the last two years. Even though the sandbox was (and still is) not feature complete, this was mainly done to ensure that it does not cause stability issues and to collect real-world performance statistics. Consequently, recent V8 exploits already had to work their way past the sandbox, providing helpful early feedback on its security properties. Conclusion # The V8 Sandbox is a new security mechanism designed to prevent memory corruption in V8 from impacting other memory in the process. The sandbox is motivated by the fact that current memory safety technologies are largely inapplicable to optimizing JavaScript engines. While these technologies fail to prevent memory corruption in V8 itself, they can in fact protect the V8 Sandbox attack surface. The sandbox is therefore a necessary step towards memory safety.
The V8 Sandbox must be enabled/disabled at build time using the v8_enable_sandbox build flag. It is (for technical reasons) not possible to enable/disable the sandbox at runtime. The V8 Sandbox requires a 64-bit system as it needs to reserve a large amount of virtual address space, currently one terabyte. The V8 Sandbox has already been enabled by default on 64-bit (specifically x64 and arm64) versions of Chrome on Android, ChromeOS, Linux, macOS, and Windows for roughly the last two years. Even though the sandbox was (and still is) not feature complete, this was mainly done to ensure that it does not cause stability issues and to collect real-world performance statistics. Consequently, recent V8 exploits already had to work their way past the sandbox, providing helpful early feedback on its security properties. Conclusion # The V8 Sandbox is a new security mechanism designed to prevent memory corruption in V8 from impacting other memory in the process. The sandbox is motivated by the fact that current memory safety technologies are largely inapplicable to optimizing JavaScript engines. While these technologies fail to prevent memory corruption in V8 itself, they can in fact protect the V8 Sandbox attack surface. The sandbox is therefore a necessary step towards memory safety.
The V8 Sandbox has already been enabled by default on 64-bit (specifically x64 and arm64) versions of Chrome on Android, ChromeOS, Linux, macOS, and Windows for roughly the last two years. Even though the sandbox was (and still is) not feature complete, this was mainly done to ensure that it does not cause stability issues and to collect real-world performance statistics. Consequently, recent V8 exploits already had to work their way past the sandbox, providing helpful early feedback on its security properties. Conclusion # The V8 Sandbox is a new security mechanism designed to prevent memory corruption in V8 from impacting other memory in the process. The sandbox is motivated by the fact that current memory safety technologies are largely inapplicable to optimizing JavaScript engines. While these technologies fail to prevent memory corruption in V8 itself, they can in fact protect the V8 Sandbox attack surface. The sandbox is therefore a necessary step towards memory safety.
The V8 Sandbox is a new security mechanism designed to prevent memory corruption in V8 from impacting other memory in the process. The sandbox is motivated by the fact that current memory safety technologies are largely inapplicable to optimizing JavaScript engines. While these technologies fail to prevent memory corruption in V8 itself, they can in fact protect the V8 Sandbox attack surface. The sandbox is therefore a necessary step towards memory safety.
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
