This is why C++ needs to be retired; and why we need to use safer languages. Not even Google can write safe C++. Thankfully Mozilla have already realised this.
This is why C++ needs to be retired; and why we need to use safer languages. Not even Google can write safe C++. Thankfully Mozilla have already realised this.
These problems can easily happen in a language like Go, unfortunately. Go is memory safe for sequential code, but that guarantee does not extend to in-process concurrency - Goroutines can access shared state without synchronization, and create memory unsafety.
Rust is in completely different spectrum and doesn't compete with Go. It's much more complex, harder to write and read. That's the cost of safety. For kernel or browser engine - that's the compromise people are willing to take. For other applications - better use something else.
I respectfully disagree. Go's primary goal was to be (superficially) easy to use and familiar. Because of this, it fails to tackle the big issue with doing concurrency in most prior mainstream languages. Go has mutable state deeply baked into the language and makes it difficult to work with immutable data. Go is not necessarily easy to use if ones goal is to build correct concurrent software.
My understanding of Go's design aesthetic is that it prefers to be explicit about things that could impact performance, which is probably why it prefers simple data structures with explicit synchronization.
That gets debatable as soon as you start using concurrency, like I said. There are idiomatic patterns in concurrent Go that are very much not safe.
It has a GC, right? So use-after-free is impossible, no?
The kind of logic bug that the Rust compiler prevents due to stronger typing is typically a crash in Go, not a security issue.
A "function" in Rust can do a whole bunch of stuff that is not signaled by its type signature.
Furthermore you're talking about memory safety vs business logic bugs, two different things.
I haven't heard of this causing a problem in practice, though.
Please give us a code example.
(I haven't seen this happen, though.)
What's even worse is that Go code is not (outside of Linux/ some distros) compiled with ASLR.
None of the languages around, that are in major production use, are really completely memory safe in practise. There is always a runtime lurking under it, most times a libc (not in Go tho, IIRC) and assortment of various libraries written in memory unsafe languages and always a kernel.
While it's probably harder to exploit Go, as, as far as I know, most of the Go runtime is written in Go, it's not impossible, just a lot less likely.
The problem with protecting a C++ codebase like a JS renderer is that the attacker has HUGE amounts of control. They can literally already execute arbitrary code in the renderer, making information leaks and other techniques much easier. ASLR was never intended to protect against an attacker with such a level of control.
Yes, that way my point. There is a runtime and/or VM and/or stdlib under these "memory safe" languages and these things are never fully written in memory safe languages themselves, at least as far as the current state of affairs goes. And even then, they run on insecure hardware (rowhammer and spectre anybody?).
Would using a language with better memory safety and other guarantees like Rust be better to write these things? Most certainly, as the attack surface becomes smaller and e.g. Rust would prevent a lot of programming mistakes in the first place. Would that be a complete silver bullet, tho? Nope.
So I agree with you that using a language with C++ isn't exactly "optimal" to implement these runtimes that are meant to run untrusted code. I just didn't like that the OP declared "memory safe languages like Go" to be a silver bullet.
>ASLR was never intended to protect against an attacker with such a level of control.
ASLR is not a protection. It's an additional roadblock put in place to make things harder for attackers once shit already hit the fan.
> Would that be a complete silver bullet, tho? Nope.
I don't think anyone would disagree!
> So I agree with you that using a language with C++ isn't exactly "optimal"
Yeah, I think the distinction here is that I don't consider it "suboptimal" I consider it to be an absolute disaster. I would call rust "suboptimal" in that the language contains some soundness holes and the stdlib contains unsafe - issues, but practically still a massive improvement.
The ideal solution is to go beyond safe languages, and use formally verified compilers and interpreters.
'CompCert', for instance, is a formally verified C compiler [0]. (Strictly, it's a compiler for a very-nearly-complete subset of standard C.) (As an aside, I Googled for a formally verified Ada compiler, but I couldn't see any sign of one. I find that surprising.)
No reason the same couldn't be done for Rust, and/or its standard library. Would be a lot of work, of course, but it would close the door on some of the bugs you have in mind.
It wouldn't save you from operating-system bugs, but formally verified operating systems are a possibility too. [1]
> And even then, they run on insecure hardware (rowhammer and spectre anybody?).
True, but I think it's safe to say that insecure hardware isn't usually near the top of our practical security concerns. I imagine one can greatly reduce the risk of such vulnerabilities if high-performance isn't required, and AMD/Intel aren't the only options.
Go has a garbage collector (i.e memory safety) & race detection tooling built in. https://golang.org/doc/articles/race_detector.html
Even if a race condition bug does sneak in, it won't be as catastrophic as C/C++.
Writing concurrent code is hard in any language. There's no magic bullet & Go or Rust are not immune to concurrency bugs. If anything, tools like the race detector should always come built in and every dev working on concurrent code should use one.
Google has a C++ race detector based on the Go one and it did not detect this issue (or was ignored).
Neither of those things you listed reduce the severity of the bug, and both are available in well written C++ code as google tends to do.
Go compiles down to machine code, it has a stack and heap, it has structures on that stack and heap, and it has data races; those are all of the required components for a use after free. You can have data races compiled by the stock go compiler without the use of unsafe (also, why have race detector tooling if this is false?). It would probably be easier to exploit than here even because one wouldn't even need to bother with the ASLR pointer exfiltration they had to do here.
It may be harder to write exploitable code, but memory safety does not guarantee runtime memory safety. Especially in the face of concurrency.
Source (scroll down to the conclusion section): https://blog.stalkr.net/2015/04/golang-data-races-to-break-m...
A lot of the exploits would be rendered harmless by employing well established technique of splitting one huge monolithic app into processes with limited capabilities and communicating via simple and narrow channels. Qmail[1] is a good example of such architecture. The architectural support is already present in common OSes.
--
It doesn't matter if userspace is fully safe, when the basement looks like a Swiss cheese of security.
Occasionally you do just end up with straight-up exploitable optimizer bugs, though. The literature has some powerful techniques to let us get rid of those too over time, but it's not as simple as rewrite-it-in-Rust. One thing that might help is simple layering: if the JIT could compile to something like wasm as an intermediate target, then you would have an extra layer of defense against optimizer bugs. This is something that's been talked about, but I don't know if anyone has seriously looked into how practical it is.
Technically it was a samsung engineer that introduced the vuln.