Hacker News new | ask | show | jobs
by kevingadd 810 days ago
I'm not singing the praises of the JVM here, it's just a simple fact that if you implement your runtime in a higher level language you're exposed to a smaller number of potential vulnerabilities. Unchecked array dereferences turn into bounds-checked array dereferences; unchecked typecasts turn into checked typecasts. Null pointer dereferences turn into null reference exceptions. Etc.

Of course once you start jitting native code, all of that is off the table. Unless you jit to java/.net bytecode, I guess.

1 comments

No, you're missing the point. The whole point is you're implementing the runtime that defines the safety semantics. Your proposal is essentially "implement your JS engine GC on top of the JVM by just using the JVM's GC", i.e. don't implement the GC yourself. The unsafe code is now the JVM GC, and you've just moved the problem from "implement the JS engine's GC" to "Implement the JVM's GC", and they same problems continue to exist.

I am really struggling to understand where this gap in understanding is occurring. It does not matter what environment or language you implement a JS engine (or whatever) in. The attacker is going to attack the unsafe portion of the runtime. If you build you JS engine on top of the JVM, then the attacker is not going to attack your JS engine's runtime, they attack the JVM's.

The JVM, .NET, etc runtimes are not doing anything different to what the JS engine runtimes are doing, and aren't magically free of the same bugs. If anything they're probably doing less to protect from or prevent attacks, because they have a much much smaller attack surface (because they aren't generally exposed to everything on the internet) and the reason attackers have to target the JS engine runtime is because the JS sandbox does not allow the general system access "correct" and completely uncompromised .NET or JVM code have. Attacks on the JVM and .NET generally mean "convince the VM to load correct code that does something that a specific app/service is not meant to do but the VM generally allows applications to do", whereas a JS VM does not allow an attacker to do anything outside of the JS sandbox, so they must compromise the runtime.

It may be easier to understand if we try to present this in a different way:

JSC can be compiled as an interpreter for any cpu architecture because there is a fall back C backend for the interpreter code generator, so you can compile JSC to WASM. Then you could make a version of webkit than executed all JS through the WASM build of JSC running under the native JSC runtime. You've now built your JS engine on top of a safe runtime (WASM), but it should hopefully be obvious that an attacker is simply going to continue targeting the native JSC runtime.

People have previously shipped JS runtimes on top of .NET and the JVM. It's not a question of 'who writes the GC', it's more fundamental.

If you JIT your JavaScript down into raw native code that bangs rocks together to dereference pointers, you need to make sure your generated code handles pointers correctly. You need to make sure to get all your bounds checks right, etc.

Sure, the JVM could somehow have a 30-year-old bug in its array bounds checks. But if you're JITing javascript to an IR that doesn't have raw pointers and instead uses strongly-typed object references and bounds-checked arrays, you have automatically closed off a whole category of defects. At the point where you're saying "sure, but what if the JVM messes up array bounds checks?" you might as well be asking whether v8 can really afford to rely on read-only pages and guard pages for its security sandbox. What if the kernel is broken?

I mentioned type confusion attacks in particular because they're a class of attack that generally doesn't work against java or .net applications because values can't change type arbitrarily during execution. Local variables and parameters have known types, object type casts are checked, array elements are typechecked before being stored, etc. Obviously you pay a cost for this, and if you have threads the ABA problem rears its head, but JS is single-threaded by design.

Between hosting JS on the JVM or in WASM, WASM is probably a safer choice since it's such a constrained sandbox. But the JS runtime you're running inside of the WASM sandbox is still built in C, banging rocks together to dereference pointers. Hopefully you're running a modern security-hardened JS runtime inside that sandbox, and you haven't turned off all the security mitigations thanks to wasm's lack of page protections.

> People have previously shipped JS runtimes on top of .NET and the JVM. It's not a question of 'who writes the GC', it's more fundamental.

Yes. it is.

That's literally the whole point.

The bugs in this post are bugs in the runtime - the implementation of the Gc, the implementation of the object metadata.

If you build your JS engine on top of a safe/managed environment the attacker is not interested in attacking logic bugs in your JS engine, they're target the runtime. All you have done is move the problem from "the attacker exploited bugs in the JS runtime, how do we prevent those?" to "the attacker exploited bugs in the Java (or whatever) runtime, how do we prevent those?". The problem is that at some point any safe language (java, rust, or even - as here - javascript) has a runtime that has to be implemented in an unsafe environment, and that is what is being attacked.

The JVM and .NET are not magical, they have the same bugs - albeit with significantly less hardening and mitigations - as JS engines.

What you are saying is that the JS engine should be written in Java (or whatever) so it's safe. But now how do you fix the JVM? Maybe rewrite that in C#/.NET? But then you have to fix the .net VM? Maybe rust? of course then we need to ensure that's safe so we should run that all under wasm. Of course that means your back at the JS engine you started with.

There's a Java-in-Java implementation called GraalVM, if I'm not mistaken. So yes, if you are that worried about bugs in the JVM, you'd use a type-safe JVM too, and then compile your JS down to java bytecode.