4 ms·
Disclaimer: I have very little experience with C++, a bit more with Rust code that bridges with JS in a manner similar to the example, and zero experience with
by eyelidlessness 3y ago
Disclaimer: I have very little experience with C++, a bit more with Rust code that bridges with JS in a manner similar to the example, and zero experience with V8 dev. All of that said…
I think the technically correct responses you’ve gotten so far may be missing an insight here: wouldn’t the V8 example code be just as safe as the equivalent JS if it used the JS array’s own semantics? More to the point: presumably those JS semantics are themselves implemented in C++ somewhere else, and this example is reimplementing an incorrect subset of them. While it’s likely inefficient to add another native/JS round trip through JSValue to get at the expected JS array functionality, it seems reasonably safe to assume the correct behavior could be achieved with predictable performance by calling into whatever other part of V8 would implement those same JS array semantics.
In other words, it doesn’t seem like you’re missing the point. It seems like this kind of vulnerability could be at least isolated by applying exactly the thinking you’ve expressed.
- professoretc 3y agoYou're essentially correct; in JS, if you write for(var i = 0; i < arr.length; ++i) ... then the array's length is not read once at the beginning of the loop; it is read every loop iteration. So if the code inside the loop (or inside the getter for `length` itself) modifies the length of the array, then it will be caught when the condition is evaluated. The problem is that the C++ code makes assumptions about JS (reading the length of an array cannot change the array's length) that don't hold. But it's an easy mistake to make.
- azakai 3y ago> wouldn’t the V8 example code be just as safe as the equivalent JS if it used the JS array’s own semantics? Yes, but imagine that the code we are talking about here is JIT code that the compiler emitted. If the compiler JITed code that was safe Rust then it could be safe. But JITs emit machine code and a big part of their performance is exactly in the "dangerous" areas like removing unneeded bounds checks. Say you have a bounds check in a loop. In some cases a JIT can remove it, if it can prove it's the same check in each iteration. Never removing the check would be safer, of course, but also slower. The point of the article here is that a lower-level sandboxing technique can help such JIT code: even if a pass has a logic bug (a bug Rust would not help with) and removes a necessary check then the sandboxing limits what can be exploited.