5 ms·
I see no reason we would expect them to succeed at something no one ever has https://alexgaynor.net/2020/may/27/science-on-memory-unsafety-and-security/ https:/
by Voultapher 3y ago
I see no reason we would expect them to succeed at something no one ever has https://alexgaynor.net/2020/may/27/science-on-memory-unsafety-and-security/ https://alexgaynor.net/2020/may/27/science-on-memory-unsafet...
- fleventynine 3y agoIf you're generating machine code at runtime (JIT), you can have memory bugs no matter what language your JIT is written in.
- Voultapher 3y agoWell maybe such a JIT is an inappropriate choice for a sandbox. There are under-explored safe alternatives such as isomorphic interpreters e.g. https://blog.cloudflare.com/building-fast-interpreters-in-rust https://blog.cloudflare.com/building-fast-interpreters-in-ru... personally I've written one such implementation that could hold it's own compared to a LLVM based JIT.
- infamouscow 3y agoThere's a lot of hobbyists that can compete with LLVM's JIT, and they do it without any formal training in PLT or compilers.
- olliej 3y agoThe whole point is the implementation and runtime for any safe language _requires_ code that is fundamentally unsafe. If you write a safe interpreter your safety is still dependent on the compiler and runtime for your safe language having no errors. This specific bug is in the implementation of a safe language, and the same can happen in any other (safe) language. People file bug reports on rustc, clang, llvm, etc all the time, and the only difference is that targeting them as an attack vector is not not particularly valuable as it's much harder to get a target to run your attack code, and the methods for doing so are much harder to hide. Note that these errors do not have to be any kind of safety error in the compiler for those languages, they can easily be erroneous codegen. These things happen. As another example, I've seen use after free bugs in WebKit and JSC come up on HN over the years, and people blame C++ memory management, when the use after failure is incorrect management of the JS GC state in the GC runtime. If you have a GCd language, then code written in that language is definitionally incapable of making memory management errors, but if you are implementing the runtime that provides that safety you are inherently and unavoidably doing things that are "unsafe" under the definition of the hosted language. The decision of many people is that JS needs to be fast, but if you disagree you can always see what the perf delta is. IIRC for non-hot code the JSC baseline JIT is maybe 10x faster than the interpreter (which is written in a pseudo assembly, so it's not taking any C/C++ overhead), and if the code is hot enough that it starts getting optimized that number just gets larger. I'm not sure what language you were compiling where your interpreter was holding its own to LLVM, but the options that would make that plausible all imply the dominant component of the runtime was not the code being interpreted. e.g. along the same lines of people comparing python performance by benchmarking C and fortran math libraries.
- Voultapher 3y agoEquating the provided memory safety of a Rust program without direct unsafe usage to a complex JIT built in C++ seems willfully ignorant. The attack surface would still be the mostly formally verified Rust type system and standard library https://plv.mpi-sws.org/rustbelt/ https://plv.mpi-sws.org/rustbelt/.
- kldx 3y agoFor the JS lifetime machinery that's entirely out of rust's borrow checker shouldn't one verify the properties of unsafe code (which I imagine the garbage collector would use)? As I understand it, Rusthornbelt exists to prove unsafe code at the cost of manual verification. As someone who's a novice in verification using model checkers, writing proofs for simple algorithms is hard enough already, so I don't see the viability of a fully verified JIT. Is there a CompCert-C equivalent for V8/JSC?
- Voultapher 3y agoThe isomorphic interpreter that I was talking about, and as shown in the cloudflare blog, does not generate new machine code. All it does is jump around in the statically verified safe Rust code. So it is extremely robust. Even if there are logic bugs in it, they will not manifest as UB.
- technion 3y agoInterestingly Edge gives you the option to disable the JIT wholesale. https://support.microsoft.com/en-us/microsoft-edge/enhance-your-security-on-the-web-with-microsoft-edge-b8199f13-b21b-4a08-a806-daed31a1929d https://support.microsoft.com/en-us/microsoft-edge/enhance-y... I've had this in place on some workstations and I've never actually noticed a performance difference.
- throwitaway1123 3y agoYou can do this in Safari too, it's a system setting called Lockdown Mode. In desktop Chrome it's a command line flag, and in Firefox it's in the settings page: https://web.archive.org/web/20230628134258/https://lemmy.eus/post/7660 https://web.archive.org/web/20230628134258/https://lemmy.eus...
- vlovich123 3y agoIs your implication that the problem is that v8 is written in c++? I can’t find the details for the vulnerability and while it may be possible this is the problem the exploit could live in the JIT itself (that’s common for vulnerabilities within v8 itself since v8 has a very thin runtime). We don’t have any memory safe techniques for the output of JITs (or compilers by the way) - even if it were written in Rust or Java it’s possible the vast majority of security exploits in this space would still exist.
- mike_hearn 3y ago> We don’t have any memory safe techniques for the output of JITs (or compilers by the way) That's not quite true. BTW there is a writeup by Exodus Intelligence here: https://blog.exodusintel.com/2024/01/19/google-chrome-v8-cve-2024-0517-out-of-bounds-write-code-execution/ https://blog.exodusintel.com/2024/01/19/google-chrome-v8-cve... It's a horrifyingly complicated vulnerability that takes 28 printed pages to explain and one line of code to fix. I don't even want to know what kind of effort it took to discover this one. The bug is not directly caused by V8 being written in C++. It's a miscompilation, and those can happen whatever language your JIT is written in. But. But but but. It could be argued that the problem is indirectly related to it being written in C++. It's a tricky argument, but it might be worth trying. I will now try and make it. Please be aware though, that I am commercially biased, being that I work part time at Oracle Labs on the Graal team who make a JS engine (open source though!). It should hopefully be obvious that these words and thoughts are my own, not indicative of any kind of party line or anything. So, yeah, take it all with a solid dash of table salt. One of the reasons a higher level language like Java or even Rust is more productive than C++ is the capacity for building higher level abstractions. Specifically, Java has very good reflection capabilities at both runtime and also (via annotation processors) compile time. High level and safe abstractions are one of the key tools used to make software more robust and secure. If you wade through Exodus' explanation, the core of the bug is that whilst implementing an optimization in very complex code, someone forgot to update some state and this causes the generated IR to be incorrect. This sort of bug isn't surprising because V8 has several different compilers, and they are all working with complex graph-based IR in a low level and fairly manual way. In the beginning the GraalJS engine worked the same way. JS was converted into bits of compiler IR by hand. It was laborious and slow, and unlike the Chrome team, the engine was being written by a little research team without much budget. So they looked for ways to raise the level of abstraction. There are very few fast JS engines. There's V8, JavaScriptCore, whatever Mozilla's is called these days, and GraalJS. The latter is unique because it's not written in C++. It's not even just written in plain Java (if it was, this argument wouldn't work, because the vuln here isn't a memory corruption directly in V8's own code). GraalJS is written in something called the "truffle dsl" which is basically using Java syntax to describe how to create fast JIT compiling virtual machines. You don't directly implement the logic of a VM or compiler for your language in this DSL. Instead you express the language semantics by writing an interpreter for it in Java whilst also using annotations, a class library, a code generator that comes with the framework and a set of compiler intrinsics to define how your language works and also (crucially) how to make it fast through tricks like specialization. The code of the interpreter is then repeatedly fused with the data/code of the program being interpreted, partially evaluated and emitted as compiled machine code to execute. This process doesn't guarantee the absence of miscompilations. Obviously, there's still a compiler involved (which is also written in Java and also uses some self-reflection tricks to be written in a high level way). But fundamentally, you aren't manually manipulating low level IR to implement a high level language. Nor are you writing several compilers. There's one compiler that's general enough to compile any language. There is a bit of a performance sacrifice from doing things this way, in particular in terms of warmup time because partial evaluation isn't free. But this does let you minimize the amount of code that's doing risky IR transformations! It's about as close as you can get to a "memory safe technique for the output of JIT". A JS engine built using this technique cannot corrupt memory in the JS specific code, because it's not emitting machine code or IR to begin with. That's all being derived from a reflection of the interpreter, which is itself already memory safe. Also, obviously, working with complex graph structures in C++ is super risky anyway, that's why Chrome uses a garbage collected heap. V8 is super well fuzzed but if you don't have Chrome's budget, implementing a JIT in standard C++ would be quite dangerous, just from ordinary bugs in the parser or from UAFs in graph handling. Like I said, this is kind of a tricky and perhaps weak argument, perhaps analogous to arguing about safe Rust (which still has unsafe sections), because there are still graph based optimizations in Graal, and they can still go wrong. But you can at least shrink those unsafe sections down by a lot.
- olliej 3y agoMy comment was more in the vein of every security bug that gets reported is followed with comments along the lines of "don't they fuzz their software", or languages like Zig that aren't memory safe but claim "debug allocators" and "support for fuzzing" obviate the need for memory safety. A memory safe language does not need fuzzing to try and prevent memory safety errors from causing security vulnerabilities, as definitionally such errors are not exploitable. Obviously fuzzing is still valuable in such an environment because crashing at runtime is a bad user experience, but it's still superior to "trampling through memory with wanton freedom and continuing to do whatever attackers want".
- znkr 3y agoThat’s a dangerous line of thinking: memory safe languages can and do have memory safety bugs. There are many causes for this, from incorrect compilation (as seen here IIUC), bugs in the language runtime, concurrency problems, to unsafe language features. That doesn’t mean that there is no value in memory safe languages, they are a definite improvement. But, they are not the solution to all problems around memory safety. For example, even in Rust, which provides some of the strongest memory safely guarantees, there are memory safety issues discovered in the standard library.
- pornel 3y agoChrome had many vulnerabilities that could be avoided with a safer language, but complex bugs in JIT are not one of them. I think you could argue that the VM should be implemented in a less dangerous way that can be formally verified or better isolated, but that's not as simple as a rewrite in Rust.
- StressedDev 3y agoThank you for writing this. I am amazed that people are still surprised that systems have security bugs and get hacked. There are lots of security bugs in every major operating system, database, browser, framework, etc. These bugs are not there because the teams building these systems want them to have bugs. They are there because security is hard and no one knows how to build large systems with no security bugs.