9 ms·
Without knowing more there's zero reason to blame C++. This looks like it implicates (a) a questionable but quite intentional performance optimization and (b) p
by wahern 8y ago
Without knowing more there's zero reason to blame C++. This looks like it implicates (a) a questionable but quite intentional performance optimization and (b) possibly a V8 GC race or otherwise an issue with reference consistency between the C++ code and the JIT'd JavaScript runtime. Both of those issues would exist regardless of the language chosen--D, Erlang, Java, Python, Rust, w'ever.
Remove V8's JIT and GC and then one can have a meaningful discussion about memory safe languages. But remove those and the vast majority of Chrome bugs would also disappear. Those are the places of highest complexity while also being the areas which no language offers any meaningful assistance.
And this is why I think it's crazy that the WebAssembly folks are dead set on a specification that presumes and requires tight, direct object bindings between the host and the WebAssembly VM, particularly wrt JavaScript GC integration, as opposed to using indirect references with a clear contract that employ proxy objects for anchoring--in effect a sort of runtime sandboxing which falls short of IPC but which substantially circumscribes system complexity (e.g. invisible data dependencies) and substantially preserves the ability for memory-safe host languages (compilers and runtimes) to meaningfully ensure their invariants.
What we need more than yet another memory-safe language is the motivation and experience in multi-language integration. What we're getting are another generation of programmers recapitulating many unforced errors. Memory-safe languages aren't new. Nor are memory-safe and ergonomic languages. Nor is, for that matter, the thinking that one can throw a single language at a complex problem despite the entire history of computer science being an unending parade of new languages and optimization models with poor integration stories. Bare C FFI is only the most minimal and easiest requirement to get correct when it comes to integration. There's so much more to the problem.
- staticassertion 8y ago> there's zero reason to blame C++ Seems like UAF in C++? So, yes, I feel comfortable blaming C++. Anyways, I didn't mention memory safe languages in my post, and I'm not even suggesting that Google did anything wrong, or should change their posture in any way. They've been extremely successful.
- wahern 8y agoUAF by whom? If it's through a bare reference held in the JavaScript VM where operations don't access the C++ object as a C++ object or as some shared primitive object as in the JVM or CLR, then the error is in maintaining state consistency across distinct environments. Such bugs look exactly the same in every language, although the risk is compounded by (a) JIT'd environments and (b) asynchronous execution. In the context of a bug involving JavaScript ArrayBuffers (where JIT compilers are designed to optimize away both FFI accessors and bounds checks) and asynchronous events I think it would be prudent to withhold judgment.
- tptacek 8y agoCan you provide an example of a dangling reference of any type (not just a UAF) being exploitable for RCE in a managed language like Java, Rust, or Go?
- microcolonel 8y ago> Can you provide an example of a dangling reference of any type (not just a UAF) being exploitable for RCE in a managed language like Java, Rust, or Go? The point is that it doesn't matter what language is hosting the JIT. You can write this same bug in a program with a JIT hosted on the JVM, in a Rust program, or in a Go program. The code generated by the JIT is the equivalent of C FFI or an unsafe block, which completely ignores your "managed" language. V8 calls directly into and out of the JIT heap with a couple stubs a handful of instructions long, code in the JIT heap is whatever you generated, and can do whatever you told it to.
- pcwalton 8y agoOK, but this bug isn't in jitcode.
- wahern 8y agoI never said it was. I said it implicated JIT'd code and the complex GC subsystem. The context is a JavaScript ArrayBuffer, the very type of object that JIT engines are built to optimize access to, and thus it's reasonable to infer that there may be some sort of cross-runtime scoping aspect at play. Such code by definition not only bypasses language protections, most relevant to this case it implicates interfaces that, in the parlance of Rust, could only ever be implemented using unsafe{} blocks. Without knowing more we can't meaningfully perform root cause analysis. Imagine passing a pointer to the backing store for Rust or Go arrays across an interface boundary. Imagine a JavaScript implementation also written in either of those languages, but where the entry point interfaces prevent the language (caller or callee) from tracking and ensuring lifetime invariants across the boundary (certainly not statically, nor dynamically because we're not using a unified abstract VM like the JVM or CLR). Correct memory management necessarily relies on the programmer explicitly and manually synchronizing liveness between those distinct environments. If they get it wrong it's not Rust or Go's fault, nor can you squarely lay blame on either side of the interface boundary; it's the programmer's fault and/or poor interface design; and the likelihood of such a bug is equally likely in Rust, Go, or C++. JIT also comes into play because even if the entirety of Chrome or Mozilla were nominally implemented in Rust or Go, the compilers and runtimes of either language could not ensure correctness of the generated JIT'd code nor track dependencies across native Rust/Go code and the generated JIT'd code--particularly for JavaScript code where the norm is object lifetimes escaping function scope. JIT'd native code effectively, if not formally, results in two distinct, disjoint runtime environments. Correctness, again, would be predicated on correct use of interfaces that might look suspiciously like malloc/free. The better interfaces would expose and emphasize the semantics of anchoring and referencing binding directly, but such interfaces are difficult to design and implement--after all, if they were then the argument that safe C++ is too difficult in practice wouldn't have such weight. There are entirely interpreted JavaScript engines. Rhino is a obvious example for Java. Duktape is a nice, embeddable interpreter written in C. Theoretically it could be rewritten in Rust, but that wouldn't make it any more likely to displace implementations like V8. And that's my point. In reality the fundamental problem isn't that we're not using a Rust-written Duktape (it's manifestly not a realistic expectation, not in the domain of JavaScript engines), it's that we don't invest enough attention in integration across inevitable language boundaries.
- pcwalton 8y ago> Both of those issues would exist regardless of the language chosen--D, Erlang, Java, Python, Rust, w'ever. Not necessarily. If the language provides a way to ensure memory management of DOM objects is correct across language boundaries, it would provide an effective defense against these issues. Safe memory management across a combination of managed and unmanaged code is a tough problem, and Rust (and perhaps Swift?) are the only languages I know of that have any attempts to solve it. It requires both language and library support. There are several competing solutions in the community: luster [1], shifgrethor [2], and josephine [3] come to mind. All of them allow this kind of code to be implemented safely. [1]: https://github.com/kyren/luster https://github.com/kyren/luster [2]: https://github.com/withoutboats/shifgrethor https://github.com/withoutboats/shifgrethor [3]: https://github.com/asajeffrey/josephine https://github.com/asajeffrey/josephine
- throwaway12iii 8y agoFrom the rust Oxidization page (hip name): - "Crossing the C++/Rust boundary can be difficult." - "For components that are relatively standalone, with small and simple APIs." - "This minimizes the C++/Rust boundary layer issues." Not only does Rust not have a good JavaScript integration story, it doesn't have a good C++ integration story. Most of Firefox is still (8 years later) not written in Rust. Chrome with C++ has a waaaaay better safety record than Firefox. Guess what? The C++ in Chrome tries to ensure memory safety for JS. The C++ Chrome tooling does a better job of memory safety as a whole system than Firefox.
- vthriller 8y ago> Most of Firefox is still (8 years later) not written in Rust. <nit>In part because 1.0 release of Rust popped up in 2015, not 2011.</nit>
- krageon 8y agoYou make a lot of points about Rust and mention Firefox in your conclusion, right after saying Firefox is by and large not Rust. I think your logical argument would be better served by using clear language and a single subject. After you have those two things you can think about coming up with examples.
- pjmlp 8y agoRegular PC world has plenty of experience in multi-language integration. SOM in OS/2, COM/UWP on Windows, JVM, .NET, ILM and TIMI on IBM mainframes. Only UNIX world has been relatively poor in this regard, sticking with bare C FFI.
- titzer 8y ago> Remove V8's JIT and GC and then one can have a meaningful discussion about memory safe languages. But remove those and the vast majority of Chrome bugs would also disappear. Those are the places of highest complexity while also being the areas which no language offers any meaningful assistance. No, just no. The data just does not back this up. The vast majority of Chrome bugs are not due to the V8 JIT or GC. Full stop. There are JIT bugs, and there are GC bugs. Some are horrible, exploitable. But on balance they are not the majority, and they often take a lot of expertise to find and exploit. Many, many critical bugs are due to array buffers and lifetime management issues of their backing stores. E.g. just tricking one code path to get the length of an array buffer wrong, and suddenly you've got write access to all of memory. (E.g. I recall a bug we had where a user program could install a JS function as a getter for the 'byteLength' property on an array buffer. One codepath in V8 trusted the result of calling this getter rather than an internal lookup, and boom). But array buffers, JIT bugs, and GC bugs aside, there are just a crapton of bugs that are directly due to manual memory management in C++ and all the other pitfalls of C++. We'd be on much better footing to get rid of C++ altogether, as it is by far the most common source of bugs in Chrome. This is the reason for Oilpan/unified heap. Humans suck at managing memory. Let machines do it, IMO. > And this is why I think it's crazy that the WebAssembly folks are dead set on a specification that presumes and requires tight, direct object bindings between the host and the WebAssembly VM Well feel free to make a proposal. (btw, running out-of-process was one of the selling points of NaCl. It did not work well with web platform API integration. A long, complicated story. The NaCl folks work on Wasm these days).
- blub 8y agoInstead of "getting rid of C++" in a project written almost exclusively in C++, it would seem more sensible for Google to just wait for Firefox to be completely rewritten in Rust in the next ~10 years and if it's really that much better, fork it, add some googley stuff on top like user-tracking and mandatory log-in and then release it as the new Chrome.
- stouset 8y agoAlternatively, since Rust natively interops with C and C++ pretty straightforwardly, it should be possible to write new components in something like Rust and opportunistically rewrite old components on an as-needed basis.