Its kind of sad that text and image documents today are so complicated that security vulnerabilities in software that reads them are considered inevitable.
Complexity is inevitable. The problem (well, one of the largest problems) is that we have collectively failed to come up with effective mitigations for these basic memory safety issues (other than sandboxing). At best we don't invest enough in effective solutions; at worst we incorrectly convince ourselves that "good programmers" never create exploitable security vulnerabilities.
Sandboxing is great. Isolate certain functionality in a separate process, sandbox it with OS-provided solutions like seccomp-bpf or capsicum, make it terminate on a violation, design its API to be idempotent so you can just restart the process on failure. Have rlimits on process memory usage. There are technical solutions and development processes to write better, more secure software.
The problem is of course that software has to be designed for this, it's not something that can easily be slapped on as an afterthought. It demands more of the design, and that's not easy to motivate in most organisations. Forcing the developer to take security into consideration would be better, but the root of the problem is that most organisations don't consider security that important, even if they say they do.
It'd be much, much nicer if we could just use memory safe languages all the time, everywhere. Sandboxing is ultimately a kludge: a recognition that our tools are so dangerous that failure is not just a possibility but an inevitability. It comes at a huge cost in developer productivity, code complexity and sometimes runtime performance suffers too.
pcwalton works on Rust, I believe. A rendering engine written in a memory-safe style is something humanity just needs to build and I applaud Mozilla in at least starting down that long road with Servo.
That said, HTML5 has sprawled to the point that it appears (to me) essentially unimplementable from scratch unless you are Google, Microsoft or Apple. That seems like a problem. If web servers indicated what HTML features a page they were serving needed, it'd let browsers pick different rendering engines on the fly, but of course ... malicious web pages would simply always ask for the least secure engine. There doesn't seem to be a good workaround for that. It's the downside of trying to force the web into being an app platform.
It's also unclear to me how the balance between safe and unsafe code works out in Rust. Looking at Servo it appears that unsafe code is used all over the place including in very core parts:
My guess is that Rust's memory model simply requires unsafe code more often than a traditional GCd range checked language does. A part of me wishes the Java guys would hurry up and get value types done already (it's years away), as combined with a lot of the auto-vectorisation optimisations being added at the moment by Intel it feels like at that point a performance competitive rendering engine written in entirely memory safe code would become realistic. Perhaps it even already is, unfortunately none of the above mentioned companies are interested in trying and nobody else can afford HTML5.
This line of thought is how I ended up concluding we need an entirely new mobile code oriented app platform.
> Looking at Servo it appears that unsafe code is used all over the place including in very core parts:
That's a very misleading characterization. It's less than 1% of the code. And it should be in core parts: those are the parts that are carefully audited and the parts that safe abstractions are built around.
We need unsafe primarily for the FFI layer, just as you do in any language. Bindings to a JIT would be no less safe in Java. You just have to write "unsafe" to call FFI functions in Rust, because we want to make it easier to audit that code.
> combined with a lot of the auto-vectorisation optimisations being added at the moment by Intel it feels like at that point a performance competitive rendering engine written in entirely memory safe code would become realistic
Any compiler developer will tell you that pinning your hopes on autovectorization is a lost cause. And you need a lot more than SIMD to write a competitive browser engine. (SIMD isn't the reason we write unsafe.) What I'd like to know is how you propose to write a JIT in safe code, without trusting that the JIT is implemented correctly. There are ways to potentially do that in theory (proof carrying code), but they're way beyond anything you've mentioned.
It may be less than 1% of the code by lines, but it's a large codebase. My point was that simply searching for the word "unsafe" in the code threw up 21 pages of results. That's not a rarely used feature.
By "core code" I meant the first results are things like layout code, a file called string_list.rs, string_multimap.rs etc. That doesn't sound like stuff at the edges to me.
Any compiler developer will tell you that pinning your hopes on autovectorization is a lost cause
I didn't mean that's the only thing that's needed, I think value types would be by far the biggest needle-mover there. But there's quite a lot of auto-vectorisation going on in the latest JIT compilers, a surprising amount (some of it not released yet).
What I'd like to know is how you propose to write a JIT in safe code, without trusting that the JIT is implemented correctly
Obviously if you're compiling code, the compiler has to generate safe outputs. But take a look at how the Graal/Truffle frameworks do it. Graal is a JIT compiler implemented entirely in Java. It generates machine code that's then handed off to the JVM for execution. Truffle sits on top and uses aggressive partial evaluation to convert AST interpreters for dynamic scripting languages into JIT compilers automatically. They have a Javascript implementation that's comparable to V8 in speed, and you can interop with it through safe APIs. Again, the entire compiler/JIT stack is written entirely in a safe language: eventually the CPU jumps to the result, but the code itself is all written in a safe way.
I wrote the layout code you're talking about. There is no unsafe in the layout algorithms, just in the work-stealing deque and the logic to share DOM nodes over to the layout thread. You can think of both as low-level generic memory management primitives, essentially as a GC. We do not allow the layout logic to be unsafe.
As for string lists and such, those are core collections. Again, those are low-level primitives and relatively easy to prove safe. We should be migrating more of that code to the safe language, but it never causes problems in practice. That's because the "build simple abstractions out of unsafe primitives and keep the complicated code safe" approach works. (We have empirical evidence that it works, you know. There are incredibly few segfaults in Servo relative to any other browser engine we've worked on.)
I am convinced that writing in Java would not make the browser any safer. All you'd do is to move some unsafe code from the engine to HotSpot. The only real effect it would have is that the word "unsafe" would not appear as often in the code.
> It generates machine code that's then handed off to the JVM for execution.
And there is absolutely no difference between that and what Servo/SpiderMonkey does, because you're trusting the millions of lines of code in HotSpot. Servo is just being explicit about the lack of safety when touching the JIT.
> It'd be much, much nicer if we could just use memory safe languages all the time, everywhere. Sandboxing is ultimately a kludge: a recognition that our tools are so dangerous that failure is not just a possibility but an inevitability. It comes at a huge cost in developer productivity, code complexity and sometimes runtime performance suffers too.
I disagree. Both Java and .NET are memory safe and both have supported some form of VM-level isolation for many years. Sure, the kind of missed-length-check-leads-to-complete-compromise we're used to from C is very rare, but at least Java is full of hole anyway.
Process-granularity sandboxing gives a clear hardware-assisted security boundary that can be quite difficult to break through.
tl;dr memory safety is great but it does not replace sandboxing.
The problem is that there's a lot of kernel attack surface, and, moreover, anything big enough to render the Web needs a huge IPC layer between a trusted chrome process and an untrusted content process, which is ripe for attack. (The latter is how most of the Pwn2Own exploits worked.)
I fully agree, but I don't think we should stop at sandboxing. Pwn2Own has compromised too many sandboxed browsers for comfort. I think we also need to have a robust mitigation strategy for memory safety problems.
Web browsers were full of security holes in simpler days too. Security gets in the way a lot if you have to take it seriously, especially when you're surrounded by C culture. By now everyone's used to the vulnerabilities.
Web browsers are one of the most complicated pieces of software on your computer. Many webpages are now webapps, and more comparable to programs than text documents.
This is not necessarily a bad thing as it allows much more interaction between user and server. But complexity often means more bugs, and thus more vulnerabilities.