Hacker News new | past | comments | ask | show | jobs | submit login

I'll be that guy.

Chrome has probably invested > 1 billion dollars into their codebase at this point. Certainly >100million into security.

They sandbox their code aggressively. They build this project with security in mind from day 1 - it's been architected for it.

The Chrome security team(s) has a lot of power for a product security org.

They fuzz. They invent new fuzzers. They cluster their fuzzers.

They have a world class bounty program.

They have a world class research team for finding vulns.

They invent or otherwise aggressively adopt mitigation techniques.

But someone out there did it.

Their track record for security is something to really be proud of - this is the first public ITW exploit of its type that I am aware of. But users are getting owned because of, at the very least, a Use After Free vulnerability.

Let's just collectively admit it, finally - you can't write safe C++ in a codebase this complex.

edit: (From a post below)

To be clear, I'm not saying "Chrome should be rewritten in a memory safe language", I'm saying that Chrome is an excellent project to point to, say "Wow, no one does as much to secure a codebase as them", and to follow that up with "and they still got owned by UAF".




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.


> 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).


Instead 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.


Alternatively, 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.


> 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

[2]: https://github.com/withoutboats/shifgrethor

[3]: https://github.com/asajeffrey/josephine


GraalVM also has a way to do this - the 'unmanaged' C/C++ is upgraded to be automatically memory managed on the fly by compiling pointers into (gc'd reference, integer field offset) and then redirecting allocations to GCd heaps. There's more to it than that of course, but it means traditional C/C++ using new/delete and GCd languages can interop and coexist in the same process and heaps, and memory management errors in the unmanaged side are caught (cause VM termination).


If you want GC there are available GCs for C++.


You are forgeting SOM and COM/UWP.

Actually something where Rust still fails short of being an alternative to Delphi/C++ on Windows, regarding tooling and productivity.

Looking forward to the HoloLens collaboration or Microsoft now pushing for Rust on their security guidelines will actually improve that experience.


From 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.


> 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>


None of this is responsive to my points.


Rust is bad at managing DOM memory in Firefox.


You 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.


> 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.


UAF 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.


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?


> 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.


OK, but this bug isn't in jitcode.


I 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.


Yes, there has to be some unsafe glue code somewhere. So what? You write it once and wrap it in a safe interface (there are at least three relevant options in the crate ecosystem that I mentioned). That way, programmers who hack on FileBuffer don't have to worry about introducing zero-days like this.

This is not feasible in C++, because the language isn't safe to begin with.


JIT isnt unsafe glue. The very core concept of JIT breaks all compiler guarantees at runtime.


To be clear, regarding my rant about WebAssembly, it would be nice to see a WASM specification that permitted, for example, pluggable WebAssembly engines. Though performance may limit it's popularity, I'd like the choice to use an engine written entirely in a language like Rust or ATS. Many people here would use it, and it would add a useful competitive dimension that emphasizes security. Also, as I previously mentioned, such an interface could limit the risk of introducing and the scope of dangerous cross-subsystem dependencies invisible to host languages and which result in memory issues.

But that can never happen if the WASM specification insists on semantics that all but require tight integration with the browsers' JavaScript engine and GC.


Yes, sure.

You write to a file, save the file name or file descriptor. Then when you read it back later, maybe you forget to validate the contents. This can be exploited depending on what you were doing with the data.

Do you now want to restrict what you mean by a 'reference'? You'll see that once you keep restricting that enough you first lose the ability to write fast programs and eventually useful programs(evidence: various 'unsafe' modes in so called safe languages).


> where JIT compilers are designed to optimize away...bounds checks

V8 does not optimize away bounds checks on array buffers. For one thing, array buffers can be detached, so their length can suddenly drop to zero.


It does for native arrays, at least:

> This is the incorrect optimization we’re looking for. JavaScript array accesses are guarded by CheckBounds nodes, which ensure that the index falls within the array bounds. If the optimizer can determine statically that the index is always in-bounds, it can eliminate the CheckBounds node. Image tying the index to the result of Object.is: since the typing information is off, we could make the analyzer think the index is always in-bounds, while it can be out-of-bounds at runtime. The optimizer will incorrectly eliminate CheckBounds, giving us OOB access to a JS array, which we can use to construct more powerful exploitation primitives.

https://abiondo.me/2019/01/02/exploiting-math-expm1-v8/

See also

https://halbecaf.com/2017/05/24/exploiting-a-v8-oob-write/

Microsoft's Chakra engine:

https://www.thezdi.com/blog/2017/10/5/check-it-out-enforceme...

Hoisting a bounds check outside a loop is a common optimization, and I would be surprised if V8 doesn't do this for ArrayBuffers. But in any event my point is that the JIT compiler is in effect doing manual memory management outside the visibility of the host language, so for these types of bugs a memory-safe host language doesn't save you. And it can get this wrong and will get this wrong because these are extremely complex systems with high code churn.


JavaScript arrays and ArrayBuffers are totally separate animals.


Regular 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.


Linux is another good example how C doesn't cut it, even among top developers.

They have a very strict patch review process in place.

They have kernel static analysers.

The kernel has been adding security gates through the years.

Yet 68% of 2018 CVE's were caused by memory corruption bugs, with the others ones left out being UB, numeric conversions, and very tiny fraction remaining of the logic error kind that are bound to happen in any programming language. [1]

Just the other day we had a Linux 5.0 pre-release bug caused by a GCC "optimization".

Oracle went with hardware memory tagging for Solaris/SPARC, Apple is following along on iOS, Microsoft security advisory (as of Blue Hat conf) is C# + Rust + constrained C++ [2], Google is pushing for hardware memory tagging, Kernel Self Protection Project, constraining what the NDK is allowed to do and Fuchsia is getting more Rust/Go modules by the day.

What is clear is that hoping for the best and the mythical 10x developers that write perfect C and C++ code won't ever happen.

[1] Source, Google talk at Linux Kernel Summit 2018.

[2] Following the Core Guidelines


I still wonder what the plans for Fuchsia are. Is Google really thinking about just throwing out the millions of man-years which have been sunk into Linux?


They don't need to please everyone, just make their use case work.

Also porting the drivers to Fuschia should be relatively easy, thanks the Treble changes that kind of made their Android Linux kernel into a kind of hybrid-microkernel.

Android is already being ported to run on top of Fuchsia.

Google isn't the only one, the IoT space is getting crowed with BSD/MIT/Apache POSIX compatible OSes, including Zephyr from Linux Foundation, which is completly unrelated to Linux.

28 years ago no would would believe that Linux distributions would eventually kill commercial UNIX.


> 28 years ago no would would believe that Linux distributions would eventually kill commercial UNIX.

That's certainly a fair point.

I was thinking more along the lines of that, at least in the scenario of consumer usage of Linux, some really interesting features are finally gaining traction from various companies (3D accelerators for instance). Was just fearing that ditching existing work for the new shiny thing would send us back to square one.


3D acceleration works just fine in Android.


The kernel is mostly drivers. If they only support a bit of hardware, they'd be tossing a lot of stuff they don't need.


And almost every operating system in the world is written in C by top developers who are in the know. If there was a better way, everyone would do it, but they don't.

Let's quit pretending that any software written in any other language would be more secure and have less bugs.


A monopoly is naturally self-sustaining. It's absurd to think there couldn't possibly be a "better way", that C is somehow perfect in its niche (that all-encompassing niche, that once covered anything and everything that wasn't grabbed by perl); But major experience in developing OS's is with C.. the knowledge, experience, tooling, etc depends on C, because C already has complete and total dominance in the area.

It's difficult to consider working on OS's in not-C, because C owns the market and ecosystem, and everything revolves around it.

But that's certainly a different reason than the absurd imagination that it's impossible to produce a language with stronger guarantees on bug-prone programming tasks (there are many languages that do, some with GC, some with stricter typing, some with static lifetime tracking, and so it goes on). That our CS theorists are so utterly incompetent that they couldn't manage to come up with even a single useful improvement to the divine language, 50 years after the great wizard Thompson etched its wisdom into a core dump

Let's quit pretending that (continued) market dominance correlates to (continued) quality.


If there is a safer, cheaper, faster language to use, someone would be using it. For another language to take C's place, it needs to be at least two of those--maybe all three and more. Market dominance, if there is such a thing in this area, has nothing to do with it.


Market dominance can be read as ecosystem dominance, and that reading obviously exists. More specifically, the C language could be completely worthless on its own, but made good enough to be usable through tooling (eg fuzzing, valgrind, etc). As it turns out, theres a lot more involved in writing an OS than just the language; and its all so conjoined at the hip, enough so that it makes it quite difficult to change just the language. But this is an argument that despite C’s flaws, its existence is on the whole preferrable to a total rewrite.

>If there is a safer, cheaper, faster language to use, someone would be using it

someone is using other languages. Urbit, redox, lisp machines, mirage are all not-c systems/OSs. But you aren’t talking about writing an OS, you’re talking about writing a popular OS. Of which there are basically three worth noting, all of which happen to have sprung up around a similar timeframe, coincidentally when C was at its most popular...

You seem to have confused the software industry for some kind of meritocracy... it is not. It follows trends harder than than the fashion industry. Look ...anywhere... for examples: OS, DB, language, game design and tooling, the AI winters, the .com crash, everything about the web, HN itself, etc. Hell, your own argument is just bandwagoning, with nothing about the technical merit (smarter people than I are using it, thus I should... is exactly how we end up in this state)


Let stop hand waving C security exploits caused by top developers, in spite of best practices.

C only got outside UNIX in the mid-90's.

Its ubiquity is an historical accident, by no means permanent, and thankfully some vendors are finally walking away from it, as proven by Microsoft security advisor for future Windows development best practices.


I'm pretty sure C was pretty well thought out and wasn't used by accident in any way. After almost 50 years of usage, I'm not sure we can say it's "by no means permanent" either.


C only got where it is thanks to Bell Labs not being allowed to sell it commercially, thus giving it away for a symbolic price to universities alongside source code.

Those university students went out to found startups that created the UNIX workstation market, like Sun.

Had Bell Labs been allowed to sell UNIX, C would have been a footnote in systems programming languages.

Instead gratis won and we got the JavaScript of systems programming.

EDIT: This is how well C was thought out.

"Oh, it was quite a while ago. I kind of stopped when C came out. That was a big blow. We were making so much good progress on optimizations and transformations. We were getting rid of just one nice problem after another. When C came out, at one of the SIGPLAN compiler conferences, there was a debate between Steve Johnson from Bell Labs, who was supporting C, and one of our people, Bill Harrison, who was working on a project that I had at that time supporting automatic optimization...The nubbin of the debate was Steve's defense of not having to build optimizers anymore because the programmer would take care of it. That it was really a programmer's issue.... Seibel: Do you think C is a reasonable language if they had restricted its use to operating-system kernels? Allen: Oh, yeah. That would have been fine. And, in fact, you need to have something like that, something where experts can really fine-tune without big bottlenecks because those are key problems to solve. By 1960, we had a long list of amazing languages: Lisp, APL, Fortran, COBOL, Algol 60. These are higher-level than C. We have seriously regressed, since C developed. C has destroyed our ability to advance the state of the art in automatic optimization, automatic parallelization, automatic mapping of a high-level language to the machine. This is one of the reasons compilers are ... basically not taught much anymore in the colleges and universities."

-- Fran Allen interview, Excerpted from: Peter Seibel. Coders at Work: Reflections on the Craft of Programming


Modern C is very different to K&R's C. Which itself was based other programming languages before it (like B - got to love their naming convention for programming languages!). But the history of C aside, it wasn't the only language that operating systems were built on. LISP, Pascal and obviously assembly / machine code too. In fact Pascal was a very popular systems language on home computing in the 80s and early 90s. If I recall correctly it used heavily by Microsoft and Apple too.

Don't get me wrong, I do like C. But it wasn't the run away success nor holds quite the monopoly you suggest it does.


Humans are fallible. Relying on them to make the same safety decisions afforded by languages such as Rust is asinine.


That is exactly why we need safety rails.


Let's just collectively admit it, finally - you can't write safe C++ in a codebase this complex.

Do they distrust their own coders to the same degree they distrust the processes they sandbox? I suspect the answer to this is "no" but I would like to hear from someone who actually codes there.

I think the truth is more like this: You can write safe C++, just like you can keep a secret. It's just that the odds that you fail go up with the size of the team involved, and it does that by compounding. So by the time you get to the size of the Google Chrome team, the odds of failure are very, very close to 1.


> Do they distrust their own coders to the same degree they distrust the processes they sandbox? I suspect the answer to this is "no" but I would like to hear from someone who actually codes there.

Actually, the answer is yes. That's why the renderer process is sandboxed.


The number of people required was a function of the "this complex" qualifier.

I can write a safe C++ app on my own. I can't write Chrome on my own.


I can write a safe C++ app on my own

But only if you don't use any external libraries, once you link in someone else's code, you can no longer be sure your program is "safe".


> I can write a safe C++ app on my own >> But only if you don't use any external libraries

using an external library is not "on your own"


Can you use the STL? What boundary is considered trusted?


The point i was making is that such boundary doesn't exist - no one can write everything "on their own".

Therefore, language features to prevent a class of exploits should be a high priority when considering a project.


I can trust the compiler... I hope?



Not at all, check the Linux 5.0 bug introduced by a gcc "optimization" regarding UB.


Use CompCert! (And C instead of C++)


Don't most people link at least with basic libraries to help with I/O and other standard operating system interfaces?

Do many people really use the kernel syscall interface directly?


once you link in someone else's code, you can no longer be sure your program is "safe"

This also fits in with the "keeping a secret" analogy. Good luck keeping something secret, if it has to be shared between organizations.


> Let's just collectively admit it, finally - you can't write safe C++ in a codebase this complex.

I work on Chrome. The working assumption is there always exists a bug that allows remote code execution in the renderer.


So what happened here? There's an in-the-wild exploit that's bypassing sandboxing - so there must be at least two bugs, or the sandbox isn't tight enough (which, for Chrome, would surprise the hell out of me).


"The second vulnerability was in Microsoft Windows. It is a local privilege escalation in the Windows win32k.sys kernel driver that can be used as a security sandbox escape."

https://security.googleblog.com/2019/03/disclosing-vulnerabi...


I used to work on Chrome. There have been periodic sandbox escapes, and chained exploits that escape to userland are routinely performed at pwn2own. This is just notable because there is a live exploit in the wild.


There there any public information about what the live exploit does?


Naturally, but I think maybe you missed my point.

I listed a subset of the things Chrome does to help mitigate the fact that C++ bugs will make it out. It's because they don't trust C++ that this is the case.

What is interesting is that, in the case of the first itw exploit, the root case appears to be memory safety. That despite the many millions invested, memory safety was still what an attacker went after and managed to leverage into an exploit against users.

That's all.


Bad time to ask for browser extensions on mobile then?


Slightly OT: revert the ugly design introduced in Chrome 71 (or 70).


I think it's a time that we should get rid of this notion of "perfectly safe code in the perfect world". It might be possible if the code doesn't change for million years, but that's not how the world works - things keep changing, the specs, the requirements and the users. Bugs are created every day. It's not about safety as a static property, but it is about the safety in an ever-changing world. As Bruce Schneier put it, it's a process. And yes, C++ might be bad for it, but that's only a part of the whole picture. We should look at the process rather then the code itself.


The OP’s entire point is that effectively no one has looked at the whole process in a more robust way than the Google Chrome team. They are literally at the vanguard of that issue, yet they got owned up by a well known code problem.


The real problem is that the web is now too complex to the point that it's impossible to safely implement it. And W3C keeps trying to add new features to it. At some point we have to stop and consider whether all this cruft is even necessary.


Of course all the cruft is necessary due to compatibility. (Browsers monitor usage statistics and do remove crufts when possible.)


"Polyfills" are quite common in Web development, and are also proof that browsers do a whole bunch of unneeded stuff (if it were needed, there would be no way to polyfill it).

I actually think polyfill usage is backwards: the current approach allows code targetting new features to be made compatible with old browsers, by the site providing polyfills.

I think it's much better for new browsers to be leaner, simpler and more well-thought-out than their predecessors. Such browsers can provide polyfills to allow compatibility with code targetting old features that have been dropped.

In other words, browsers should be getting smaller (e.g. a canvas, audio stream, input method and language interpreter); more and more functionality should be offloaded into interpreted libraries (parsing, text rendering, DOM, event loops, layout, Javascript, etc.)


This _did_ successfully happen with MathML. It was on track to get native browser support, but they decided it the polyfill (mathjax) was good enough and wasn't worth native support for such a niche feature.

As a web developer, I much appreciate native features and avoid polyfills as much as possible.


Polyfills often are worse (slower, only provide parts of the functionality, ...) than the native implementation provided by newer browsers, so the browser implementation isn't strictly "unneeded".


If there's ever a project as large as Chrome written in a non-C++ language, I guarantee you it will have these bugs as well -- due to use of `unsafe`, or bugs in the compiler, or what have you.


How many remotely exploitable bugs in Java apps (that don't run arbitrary Java code†) do you see that arise from Java compiler bugs? I can't think of a single one.

†I explicitly say "not running arbitrary Java code" to emphasize the difference between this scenario and JS engine vulnerabilities. JS engine vulnerabilities are a problem because JS engines execute untrusted code, but I'm talking about compiler bugs in trusted code that can nevertheless be exploited remotely.


> How many remotely exploitable bugs in Java apps (that don't run arbitrary Java code†) do you see that arise from Java compiler bugs? I can't think of a single one.

Seems like an odd restriction to impose here. How many C++ exploitable bugs arose from a C++ compiler bug? I think the answer to that is also zero. Doesn't really seem like a useful thing to consider?

But in terms of Java runtime's security it's not particularly stellar. The entire category of "Java deserialization" is the never ending fountain of remotely exploitable security bugs in Java apps that don't run untrusted code.


> Seems like an odd restriction to impose here. How many C++ exploitable bugs arose from a C++ compiler bug? I think the answer to that is also zero. Doesn't really seem like a useful thing to consider?

Right, and that's why the fact that the Rust compiler has bugs is irrelevant. The language is meaningfully more memory-safe than C++.

> But in terms of Java runtime's security it's not particularly stellar. The entire category of "Java deserialization" is the never ending fountain of remotely exploitable security bugs in Java apps that don't run untrusted code.

Java deserialization RCEs, while pernicious, are nowhere near as common as use-after-free and related bugs in C++ code. Furthermore, you can effectively mitigate them by statically banning OutputObjectStream and whatnot. There's no such easy foolproof static mitigation available for C++ UAF.


> Java deserialization RCEs, while pernicious, are nowhere near as common as use-after-free and related bugs in C++ code.

Can you justify that? Actual exploits from Java deserialization RCEs are well known & published, but by contrast searching the CVE database reveals a rather small number for C++ or CPP. Use-after-free gets a large list, but the majority seem to be in C code not C++ code.


UAF are the most common, and the most commonly exploited kind of security bugs in Microsoft products for the last decade [1]. And Microsoft products are way more C++ than C.

[1] https://www.zdnet.com/article/microsoft-70-percent-of-all-se...


Which is why their security team is now pushing for C#, Rust and constrained C++ for new developments.

https://github.com/Microsoft/MSRC-Security-Research/tree/mas...


All I can say is that more or less the entire security community disagrees with you that RCEs in Java programs are as common as RCEs in C++ programs.


I'm not sure how you'd even search for those, because deserialisation doesn't have an exact, common equivalent in C land. There are almost no cases of arbitrary/named types being constructed from arbitrary inputs at runtime. You have to take into account the really low prior probability for the counts to make sense.


Here's a specific example for a C++ compiler: CVE-2019-0546

I have a feeling you could find Java ones too if you looked hard enough. Compilers are complex beasts.

[1]: https://nvd.nist.gov/vuln/detail/CVE-2019-0546


That seems to be this one: https://www.thezdi.com/blog/2019/2/28/finding-unicorns-when-...

It's a straightforward miscompilation. I'm not sure why they even classify it as a vulnerability.

From Microsoft, per the article: "The said vulnerability is about downloading and running untrusted code, which has always existed in all releases prior VS2017 Update 9 that supported lambdas. The scenario is not common coding practice and considering we've always had this in all our prior releases and have not seen any evidences of exploit it would not make sense to port the change as hotfix from 15.9 to prior VS releases."

In other words, it's never made into a product in an exploitable way. I'd just call this a miscompilation.


> If you’re still on the fence about deploying this update, we would consider it Important since it could allow for attacker-controlled code to execute at the level of the logged on user.

What does that even mean? I download some c++ code from the internet, compile it, run it, and... it runs as my user?


https://portal.msrc.microsoft.com/en-US/security-guidance/ad...

> Exploitation of the vulnerability requires that a user open a specially crafted file which was compiled with an affected version of Visual Studio. In an email attack scenario, an attacker could exploit the vulnerability by sending a specially crafted project, or resource file, to the user and convince the user to open the file.

So yeah sure looks like a basic code execution results in code execution. Surprised this even got a CVE.


Yeah, I think it means just that.

I guess there is some conceivable exploit where you compile some hostile code written in a safe language to C++ with MSVC and then run it, and the attacker could exploit this bug somehow? But who does that?


How many remotely exploitable bugs in Java apps (that don't run arbitrary Java code†) do you see that arise from Java compiler bugs? I can't think of a single one.

If enough of a complex system is written in a language or run on a managed runtime that eliminates entire classes of exploits, then the developers will wind up implementing some optimizations and/or a way to write applications/plugins/extensions in C/C++. These mechanisms will open the doors to those exploits again.

(At least for now. Going forwards, I think we might be able to solve this one.)


Depends on if you mean Java->Bytecode compilation or JIT. For the former, I don't recall ever seeing one, for the latter I've gotten one fixed.


Was that one remotely exploitable? If so, how would someone exploit it, specifically?


Yeah, totally. I'm not saying they shouldn't use C++ (though I think they could probably save a ton of money if they did). I'm saying that they spend a lot of money on security, they do an incredible job, and stuff like this can still slip through, which I find really interesting.


In a general sense, I'd agree, but from the description in that article, it's interaction with native files or file dialogs. Even if you use Java, you're susceptible to this sort of bug, because your Java code calls into layer of C code to handle the native interaction.

Realistically, any browser is probably going to have a decent amount of unsafe code surrounding the native windowing, files, graphics, and so on.


Look at the patch; it's a use-after-free bug involving DOMArrayBuffer and ArrayBuffer, which are Chrome objects, not native OS objects.


Completely agree. The refusal of us nerds to accept that our tools can be improved and replaced is a broken record that has been playing since the dawn of programming. It reminds me of how assembly programmers rejected high level languages; even with the glaring historical repetition, we are unable to recognise our loyalty to all languages is irrational.

It’s patently obvious C/C++ is just a bad choice for many tasks, particularly where security is so important. I’ve never heard someone who works with those languages agree.

However - if you’re reading this and feeling vindicated in your particular language choice - be prepared to accept that the language you favour right now is already or will soon be in the same position. That’s progress. Even if you love Haskell or Rust or Scala or Ocaml, if you think it’s anything but a stepping stone in language development then you’re just as wrong as those defending C/C++. As far as programming languages are concerned, we still don’t know what we’re doing.

If you have managed to be open minded about languages, then test yourself further by telling yourself that emacs/vim is a local optima and will be superseded by better programming tools, and you should be exploring IDEs and imagining what a world than doesn’t code in plain text could look like. Because typing coding in text form is almost certainly not the best way to write programs, any more than assembler was.

Hard to accept, ain’t it?


Then I'll be that guy that tells you that including side channels after side channels (WebGL, WebUSB, FileStream), each with byzantine security features haphazardly tacked on, is the actual problem.


Hence why it is freaking amazing to follow what Firefox is doing with Rust.


Is there an official explanation of the kind of bug? I cannot find it.

"Let's just collectively admit it, finally - you can't write safe C++ in a codebase this complex."

Pointing the finger and saying "see?" is a pretty low quality argument.

So, I guess we should wait for the first Rust or managed language remote exploit (or whatever) to say "admit it, finally - you can't write safe... programs?".


Managed languages remove an entire class of exploits present in C and C++ programs. That alone is a very good start.


"Chrome has probably invested > 1 billion dollars into their codebase at this point. Certainly >100million into security."

Let's conservatively say Google pays 1,000 developers to work on Chrome, and on average those developers cost Google $300k each. That's $300 million a year just in payroll. Google has invested far more than a billion in Chrome by this point.


I'm amazed. Are there really, "conservatively," 1000 developers working on Chrome full-time, for years? That seems incredibly high to me, but maybe I just don't realize what it takes to build an app like Chrome.


As an estimate I think we can safely say it's not conservative. Especially if this is actually developers (ie straight up coders) and not also all of their supporting personnel.


No codebase will ever be fully free of security bugs, but having 7 million lines of code definitely doesn't help.


The question is, how much effort is it to keep users safe given language X vs C++.

Apparently, when the language is C++, the value is somewhere above many millions of dollars, and we don't know what it is.

Can we reasonably expect that price to be lower with language X? For many reasons, I would argue 'yes'.


Sure, but is your goal maximal safety no matter the cost, or are you trying to strike a balance with other factors?

If all you want is to keep users safe then it'd be easy - ban JavaScript wholesale. Bam, done, instantly made the web safer & faster. Don't even need to deal with the huge security can of worms that is WebGL, either.

But safety is rarely the exclusive factor in play, now is it? Things like memory usage efficiency & performance also play large roles here.


To be clear, I'm not saying "Chrome should be rewritten in a memory safe language", I'm saying that Chrome is an excellent project to point to, say "Wow, no one does as much to secure a codebase as them", and to follow that up with "and they still got owned".


But to what end? Pure-java apps get owned all the time, too. If you look over there you'd just give up on ever trying to transmit data, as the number of Java de-serialization exploits is nothing short of stunning.

The only semi-proven way to not get owned at this point is to never go online.


I'm not really interested in having a language shootout but I will say that I think you may be misunderstanding Java's history of serialization vulnerabilities.


> I think you may be misunderstanding Java's history of serialization vulnerabilities.

Can you elaborate? https://github.com/GrrrDog/Java-Deserialization-Cheat-Sheet is just one example compendium. The number of vulnerabilities in this area is huge, across a wide range of libraries. It really is an entire category of exploits at this point, and it's still ongoing (eg, from last year: https://nvd.nist.gov/vuln/detail/CVE-2018-4939 )


You're right that it's a real footgun, the major issues that I'm aware of are in the native serializer, but yeah even the big libraries like Jackson have had issues.

I don't consider those on the same level as memory unsafety, personally, but it's not really a meaningful comparison because I'm not going to dig around for numbers and mitigation techniques.


I suggest that the most expedient (cheapest) language to migrate the existing code base to would be a memory safe subset of C++ [1]. In practice most of the safety benefit could be obtained from a just a partial migration. Specifically, just banning raw pointers/views/spans and non-bounds-checked arrays and vectors. From a quick glance at the code in the (quite small) patch diff, the code in question includes:

    DOMArrayBuffer* result = DOMArrayBuffer::Create(raw_data_->ToArrayBuffer());
    
    ...
    
        raw_data_.reset();
    
    ...
    
   return result;
I'd imagine the effort/time/money it would take to replace those raw pointers with memory safe substitutes [2][3][4] (and enforce the ban going forward) would be relatively modest. Performance shouldn't be an issue [5].

[1] shameless plug: https://github.com/duneroadrunner/SaferCPlusPlus

[2] https://github.com/duneroadrunner/SaferCPlusPlus#registered-...

[3] https://github.com/duneroadrunner/SaferCPlusPlus#norad-point...

[4] https://github.com/duneroadrunner/SaferCPlusPlus#scope-point...

[5] https://github.com/duneroadrunner/SaferCPlusPlus-BenchmarksG...


Ug. After closer inspection, it looks like those particular raw pointers seem to be managed by a garbage collector. (Specifically, the "Blink GC" [1].) As others have pointed out, this particular bug may not actually be a C++ issue. (Or at least not a typical one.)

[1] https://chromium.googlesource.com/chromium/src/+/master/thir...


Not so sure. I haven't worked on this code for a while and have no non-public knowledge of the bug, but ArrayBufferBuilder does not inherit from any of the GCed base classes, and has the USING_FAST_MALLOC macro which is used for non-GC classes. https://chromium.googlesource.com/chromium/src/+/refs/heads/...

The ArrayBuffer itself also uses reference counters rather than the GC base classes from Oilpan. https://chromium.googlesource.com/chromium/src/+/refs/heads/...

See also https://chromium.googlesource.com/chromium/src/+/refs/heads/... and https://chromium.googlesource.com/chromium/src/+/refs/heads/...


ArrayBufferBuilder isn't, but DOMArrayBuffer seems to be a GC managed type [1], right? And, before the patch, the DOMArrayBuffer held a "refcounting pointer" potentially targeting raw_data_'s reference counted ArrayBuffer, right? I don't see any immediately apparent use-after-free with this, so I assume raw_data_'s ArrayBuffer is being messed with elsewhere? As someone who worked on the code, do you have an idea/hunch about where the invalid memory access actually occurs?

https://github.com/chromium/chromium/blob/ba9748e78ec7e9c0d5...


Yes, DOMArrayBuffer inherits via ScriptWrappable from GarbageCollectedFinalized<> so it's on the GCed heap. I don't understand the UAF yet, I'm hoping someone will write a blog post on it later :-).


It can be for a specific class of bugs. A solution doesn't need to be perfect for it to be better.


Your claim that Chrome can be used a lesson in choosing programming languages is quite weak, because almost no one will build such a complex application and despite the risk all successful browsers still chose C++.

An application that:

a) has downloading and executing untrusted code as its main feature

b) supports a ton of media formats, with DRM to boot

c) is going through rapid changes, including adding very complex low-level features like WebUSB, WebGL or WebAssembly.

... will be insecure by design.

It's also very likely that no one will create a browser in C++ again, or in any other language for that matter. Microsoft recently gave up and decided to use Chromium, Opera gave up a long time ago. Chromium itself was forked from WebKit, which is used by Safari. Firefox is the only odd one out, slowly but surely sliding into irrelevance.


You've missed my point entirely. This is not a plea to Chrome to rewrite their browser. My post has nothing to do with browsers.

My claim is that Chrome is an interesting case study in the most hardened user-facing C++ codebase in the world being hacked because of memory unsafety.

I also think that the strawman argument you've made is extremely weak for other reasons. This whole "insecure by design" thing is nonsense - everyone stating this seems to not understand that multitenant systems have existed for ages, under far more difficult constraints. An example - AWS Lambdas colocate your code with other companies' on the same hardware - if this were such an impossible task, that wouldn't be possible.

But again, my argument is not about browsers, or even much about languages, and is merely me pointing out an interesting case where memory unsafety was the root cause of an attack, despite great efforts.


The culmination of your message was "Let's just collectively admit it, finally - you can't write safe C++ in a codebase this complex.", that's your point. It's probably correct, but also meaningless as I mentioned, because:

a) most codebases aren't even nearly this complex

b) no one will likely write such a codebase again

So the entire "point" is just trivia. It can't be used to decide whether to program something in C++, it can't be used to decide whether to use Chrome, it's not actionable at all.

Frankly I'm peeved that a superficial comment triggered such a long discussion and monopolised the top spot in the thread instead of making space for technical explanations or more interesting discussion.


You're attempting to derive meaning from my post, which in your mind is that browsers should not use C++, where there is no such meaning.

That you have failed to do so is not my issue. Don't blame me for having an upvoted post.


>Firefox is the only odd one out, slowly but surely sliding into irrelevance.

That is very false.


The highest number I've seen for Firefox's marketshare is 5.8%, which is Wikipedia's metrics: https://en.wikipedia.org/wiki/Usage_share_of_web_browsers#Su...


Your last two sentences have nothing to do with language choice.


Isn't it more a case of "you have to do everything right to keep the attackers out, they have millions of attempts and only have to get one right once to beat you".


That's a weird and dishonest point to make here.

You can't write safe $ANYTHING for a codebase this complex. I am yet to see someone who has worked on a codebase this complex disagree. Security issues and crashes in general result from your program interacting with the outside world and something behaving in a way you did not account for.


What’s dishonest? No one has said language choice is a silver bullet for all classes of security vulnerabilities. The point under contention is that we have better options for memory corruption vulnerabilities in particular.

In other words we’re not talking about whether or not something will be totally safe, but rather safer. Perfect is the enemy of good.


> Let's just collectively admit it, finally - you can't write safe C++ in a codebase this complex.

Well, humans can't. And computers can't, yet.


> And computers can't, yet.

Yes they can. Computers have been writing safe code in various languages for decades: they're called compilers/transpilers. Maybe this is one of those situations where we define AI as "things we don't know how to program yet": we said being good at chess would require intelligence, once computers got good at chess they became 'just tree search'. It sounds like you're imagining that automating programming requires some currently-unknown approach, when others would say it's 'just compiling'.

Of course, we need to tell the computer what code to write. We do that using a programming language.

It could be the same language (C++), but that seems a bit pointless. Furthermore, since C++ allows unsafe code, using C++ to tell the computer what we want means that we're able to ask for unsafe things (if we want to). That makes the computer's job ambiguous:

- Should it always do what it's told, and hence write unsafe code when asked? (In which case, it doesn't solve the "write safe code" task we're talking about)

- Should it always write safe code, and hence not always do what it's told? (In which case, where do we draw the line? Would it be valid to ignore all input and always write out `int main() { return 0; }`?)

We can avoid this ambiguity if we forbid ourselves from asking for unsafe things. In other words, providing a guarantee about the code that the computer writes is the same as providing that guarantee for the instructions we give it (i.e. the input programming language).

For example, if we don't want the computer to ever write memory-unsafe code, that's the same as saying we can't ever tell it to write memory-unsafe code, which is the same as saying that we should instruct it using a memory-safe language.

That way, it's possible for a computer to not only write safe C++ in a codebase of arbitrary complexity; but (more importantly) to write safe C++ code that does something we want, and the instructions specifying what we want can be arbitrarily complex.


So what language should they've written it in?


A memory safe one. There are many of them. They could build their own if they chose to - they've built multiple languages in the past.

Picking a language for the Chrome team doesn't seem practical - we all know where your question is going to head.

The point is they have to not pick C++. Again, they've invested many, many millions of dollars into security. Let's not pretend that they're priced out of using another language.


Back when Chrome was getting started there were no memory safe languages that did not come with huge downsides.

Now one could argue for Rust, but let's not pretend that C++ was a bad choice. C++ was the overwhelmingly best choice at the time.


I didn't say C++ was a bad choice and I've purposefully avoided making this another language war issue.

What I'm saying is that Chrome is an example of a project with more security funding than just about any other project out there, and it still can't save users from the footguns of C++.

I guess the question used the wording "should have written it in?", to which I said "a memory safe one", and that's not true. I'm not interested in making a judgment call on their initial choice - I totally get the decision, regardless.


> What I'm saying is that Chrome is an example of a project with more security funding than just about any other project out there, and it still can't save users from the footguns of C++.

You can certainly make that argument but this doesn't seem to really be a compelling example. They had one security issue caused by a use-after-free over a decade. That's... a pretty fucking stellar track record and does not at all scream "you cannot use this language safely!!!", now does it?

I would certainly argue for something like Rust instead if starting from scratch, though, but just because A is better than B doesn't make B unusable trash, either.


Well, that's not really a fair interpretation of my argument, I'd say.

My argument is:

* We have a production, widely used codebase

* This codebase is in C++

* This codebase has probably the most significant efforts to secure it of anything of its size, or at least it's in the top 5

* This codebase suffered enouguh critical vulnerabilities for users to be attacked in the wild

So let's be clear - Chrome suffers from thousands of memory safety vulnerabilities, but not many make it to ITW exploits. This case is special because users actually suffered because of it. It isn't fair to say they've had "one security issue", certainly, just one that's had major user impact.

I'm not judging C++ or the choice to use it. I am saying that this is an example of a very hardened system not being able to compensate for memory unsafety.


> not being able to compensate for memory unsafety.

This is a bit of a simplistic view tbh. They have managed to drive up costs for 0 days on the black market. They have introduced auto-updating browsers to get security patches to users quickly. They have managed to reduce the number of security exploits. This is a big feat and major progress.

They have compensated for memory unsafety quite effectively. Maybe not perfectly, but well enough.

Yes, memory safe languages are wonderful. I myself am a rustacean and LOVE to RIIR. But their efforts weren't in vain.

Should they start thinking about adopting a memory safe language like Rust or Wuffs? Definitely.


> This is a bit of a simplistic view tbh.

It's a very literal one.

I think I've been very open about what an accomplishment they've made, and how proud they should be to have kept Chrome users safe for all of these years.

It is exactly because their efforts to secure Chrome are so impressive that I think this event is interesting.

None of what I've said is about Rust or even a suggestion that they shouldn't keep doing what their doing. One ITW exploit in a lifetime of a product is a great track record.

It is merely interesting to observe when herculean efforts fall down.


This is not the first chrome security issue in over a decade.


Not by a long shot either. The current stable version of Chrome (72) came with 58 security fixes, including a dozen UAFs, albeit several of those are in third party dependencies.

https://chromereleases.googleblog.com/2019/01/stable-channel...


There was even another UAF just a few months ago. https://chromereleases.googleblog.com/2018/11/stable-channel...


I wonder what are the huge downsides of Modula-2, Ada, Delphi, ....


C++ is a write only language. Too many complex features that are used in ways that only the author of the code understands at the time of writing - or thinks of understanding at the time of writing even.


That doesn't mean that they (or anyone) should still be using it.

The choice of language 15 years ago has nothing to do with the languages that could be in use today on the same project.

Rust has `unsafe` all over the place; it's no more safe if you use that keyword to do the same things that are done in C++.

Get more compiler people on Go and it will be even faster than it is now (really fast), and Go is written in Go so there's no reliance on C++, unlike Rust.


> Rust has `unsafe` all over the place; it's no more safe if you use that keyword to do the same things that are done in C++.

Yes, it is, because the language has the concept of safe code to begin with. The problem with C++ is that all C++ code could potentially be unsafe.

> Get more compiler people on Go and it will be even faster than it is now (really fast), and Go is written in Go so there's no reliance on C++, unlike Rust.

This is a very confused comment. Rust is also written in Rust. If you're thinking of LLVM, sure, the Rust compiler uses that, but LLVM is nowhere to be found in products written in Rust that are shipped to users. What matters is how safe the runtime is, and there's no real difference between Go and Rust in this regard. Go might theoretically have some kind of edge over Rust in safety in that it calls syscalls directly instead of going through libc, but I highly doubt it matters in practice, because (a) the libc wrappers are a drop in the bucket compared to the complexity of the kernel; (b) Go only does that on Linux these days anyhow.


Yup, and besides, Go has the unsafe (https://golang.org/pkg/unsafe/) package, which allows for basically the same sort of unsafety as Rust's unsafe.

In fact, I would say Rust is typically more safe than Go, because in Rust you can mark any code as unsafe, it doesn't necessarily have to involve unsafe pointers. For example in Rust FFI functions are typically marked unsafe even if they don't involve pointers. There's no way to do that in Go.

Another example is strings: Rust ensures that strings are always valid UTF-8 by marking any function that could break that as unsafe. OTOH in Go you strings can contain invalid UTF-8 if I recall correctly.


I thought Rust was written in C++, apologies.


My project currently has 104366 lines of Rust code and 174 uses of 'unsafe' (many of which are somewhat spurious, like usage of `mmap` and other system calls that don't have safe wrappers in external crates). My project does a lot of low-level stuff (e.g. ptracing, sharing memory with C++ code in other processes) and has lots of crazy low-level optimizations but still only has one use of 'unsafe' per 600 lines of code. I haven't made any particular effort to reduce usage of 'unsafe' either.

> it's no more safe if you use that keyword to do the same things that are done in C++

Sure, but using 'unsafe' everywhere that you would write unsafe C++ code simply isn't idiomatic Rust and except for very specific and limited situations, Rust developers don't do that.


> Get more compiler people on Go and it will be even faster than it is now (really fast), and Go is written in Go so there's no reliance on C++, unlike Rust.

You'll still be stuck with the fundamental tradeoffs that Go made like being GC'd and not having a fast FFI.

Those tradeoffs make sense in the target audience of Go (servers), but it's not going to make sense in something like a web browser.


Go has a garbage collector, and it's the fastest garbage collector that currently exists, by a wide margin, I believe.

When Go 1.8 released, it measured < 1ms garbage collection times on an 18GB heap, and that number has improved in the 2 years and 4 releases since then.

I don't believe that "it uses garbage collection" is a valid complaint against Go anymore, except in a real-time application, and I don't know of any real-time applications written in Go. I'm sure there are some, I just don't know of them.


> Go has a garbage collector, and it's the fastest garbage collector that currently exists, by a wide margin, I believe.

Fastest by what measure? I benchmarked some key value store implementation written in golang and ran into large (> 1 sec) latency spikes because the golang gc couldn't keep up with the large (10GB+) heap space. Java would have allowed me to select a GC algorithm that's best for my use case and I wouldn't have run into that issue.

golang's gc is tuned for latency at the expense of throughput, that's basically it. It's not some magic bullet that solved the gc issue, contrary to what the golang marketing team wants people to believe.


Go's garbage collection is optimised for reducing GC pause times, and while it is extremely fast in this regard this is only one of many ways GC performance can be measured.

If for your workload GC pauses are tolerable and throughput is important (say when doing offline CPU bound batch processing) then the Go GC is not optimal for your use case and will be slower than other options on the market.

Some runtimes (such as the JVM) allow the user to pick from one of many GCs so that they can use the one that is most appropriate for their workload.


A fast GC doesn't mean the code wouldn't have been faster without a GC.

GC's prevent you from doing a huge range of techniques to improve cache locality or reduce allocation/free churn.

A simple example would be games will do things like have just a big allocation for all a frame's temporary data, and they just bump-pointer allocate from it. Then when the frame is over, they reset back to zero. It is already The Perfect GC. Bump pointer allocation, zero pause time, and perfectly consistent memory usage. No matter how good Go's GC gets it will always be slower than that.

At the other end of things you have things like LLVM's PointerUnion, where alignment requirements are (abused) to cram a type id into the lower bits of the pointer itself. Type-safe variant in the size of a void*.


> GC's prevent you from doing a huge range of techniques to improve cache locality or reduce allocation/free churn.

This is only true in languages which don't provide other means of memory allocation.

D, Modula-3, Mesa/Cedar, Oberon variants, Eiffel, .NET all provide features for such techniques.


Fuchsia team thinks otherwise, where kernel level stuff like the TCP/IP stack and IO volume handling are written in Go.

Android team also makes use of Go for their OpenGL/Vulkan debugger.

Now, lack of generics is really a big pain point.


> Fuchsia team thinks otherwise, where kernel level stuff like the TCP/IP stack and IO volume handling are written in Go. > Android team also makes use of Go for their OpenGL/Vulkan debugger.

Neither of those examples disagree with what I said. Fuschia's usage is the perfect example of agreement - Go specializes in IO (server workloads) and is then being used to do IO. That's a great usage of Go's particular blend of capabilities.

The use of GO in GAPID would be more interesting if it had any meaningful constraints on it, but it doesn't. It's an offline debugger for something that ran on a device with a fraction of the speed.

RenderDoc would be the meatier Vulkan debugger, and it's C++.


GAPID is an online debugger and a TCP/IP stack needs to be really fast.

As early C++ adopter I always find ironic how people give examples of C++'s code generation performance.

Back in the old days, tying to use C++ would generate exactly the same kind of performance bashing.


Sorry. Until Go gets its head out of its ass and gets proper generics Go will still be painful to use and slow. Not to mention with all the extra code complexity from writing everything with interface{}{} and reflection there are bound to be plenty of exploitable vulnerabilities. I honestly think it is worse than pre-generic Java because switching over the type of the variable is encouraged. Most sane type-checked languages kinda expect you to know what type you're working with at compile time.


Generics are slower, that's the trade-off. Developer time vs. execution time.

Saying Go has it's head up it's ass is extremely disrespectful to the people that created it and are maintaining it. You lost all credibility when you chose the low road and said that.


Look, there are core Go developers who admit that parametric polymorphism is a good idea. In fact, I'm not aware of anyone on the Go team arguing against that. Which is what you mean by "generics", right? In fact Go already has "generics" - maps, slices, channels. Are those "slow" in your opinion?

The only thing left to do is to propose a system that has reasonable trade-offs, doesn't suck, and doesn't completely break the existing language. Easy peasy!

I wonder who is doing Go more a disservice - people who hate everything about, or the blind fanatical devotees who think any criticism towards any aspect of Go is heretical.


You lost all credibility when you wrote that first sentence.

But you carry on using empty interface with runtime type assertions and reflection if you think that's faster.


Well, pardon me for stating things as I understand them and making a mistake in doing so.

I sincerely apologise for wasting your time by typing something that was true as I understood it.

Clearly you've never made a similar mistake.


It was not true yet you stated it with certainty as fact.

Not interested in your snarky "apology".


Most generics work by monomorphisation, which basically means the compiler is doing a copy paste from int_btree.go to string_btree.go with the types changed. There is no runtime cost except text segment bloat. Generally modern compilers are smart enough to unify the identical machine code paths too, so you might not even get the code bloat.


> Generics are slower, that's the trade-off. Developer time vs. execution time.

You have no idea what you're talking about.

> Saying Go has it's head up it's ass is extremely disrespectful to the people that created it and are maintaining it. You lost all credibility when you chose the low road and said that.

No it's not disrespectful, it is entirely true. Do consider the origins of Go, where it all started as an experiment in combining bad decisions into a single programming language to see what would happen. What those very people didn't realize upon releasing it to the world is the sheer amount of people who fell for it, it was supposed to be a joke, with a stupid looking mascot and all... Now they have to take it seriously - and Google has to choose between keeping it alive or getting a forever bad rep for killing it - because too many companies rely on it, and they are forced to retrofit useful features on top of the giant pile of garbage they've created.


> No it's not disrespectful, it is entirely true.

Yes, it is disrespectful, and no, it is not entirely true.

If you want to talk literally, a team has no ass to shove things into.

If you want to talk figuratively, making mistakes does not constitute having your "head up your ass." Even making multiple mistakes doesn't warrant that kind of statement. It's rude and it's pointless and doesn't add anything at all except to make you look asinine. So now you're just as asinine as I am with my misunderstandings. Well done.


(I'm going to ignore the personal attack and insult)

> If you want to talk figuratively, making mistakes does not constitute having your "head up your ass." Even making multiple mistakes doesn't warrant that kind of statement.

Making deliberate mistakes multiple times and resisting fixing them for 10 years does qualify as having their "head up their ass" (or asses if that's what you prefer).


I don't know about the origin story, although frankly I hope it's true.

Personally, I think Go is terrible. However, I can't imagine Google is even remotely considering scrapping Go. It is massively popular. It is used all over the place both externally and internally. It's a huge branding asset, community outreach tool, recruiting tool and provides leverage in the form of first party libraries over the direction that software development as a whole is going.


>Saying Go has it's head up it's ass is extremely disrespectful to the people that created it and are maintaining it. You lost all credibility when you chose the low road and said that.

No, because Go really has its head up it's ass.


> Rust has unsafe all over the place

This does not match my experience nor numbers I’ve seen. What leads you to say this?

(Also, other than LLVM, Rust is written in Rust. And when cranelift lands, you could remove that too.)


Apart from memory safety issues, there are type safety issues that can cause equal security harm. Go's approach of casting interface{} back and forth is as dangerous as allowing malloc/free. The worst part is that the language designers don't see this as a problem.


interface{} is not great but it is not void *. Type assertions (casts) on interface{} are typechecked at runtime and failed type assertions on interface{} cannot violate memory safety.


Interface in Go can actually violate memory safety because of object slicing when racing on a variable concurrently from multiple goroutines (unless things have changed lately). It's two words (data and vtable) and so there is a tiny window in which one goroutine could change the data pointer while another goroutine is reading the two words, and so have a type mismatch that could probably cause arbitrary code execution in theory.

I've never heard of this happening in practice, however.


Unless I'm misunderstanding you, you're just talking about a garden variety data race on any interface value in shared memory right? If that's the case, I don't see what it has to do with interface{} in particular (or interfaces at all) as any multiword value in shared memory is susceptible to this, which is the unfortunate price you pay when your language supports shared memory concurrency but doesn't have any allowances for it in its type system.

Of course, data races are usually race conditions too, meaning that even if the memory corruption were prevented, there would probably still be a correctness bug to worry about, so I can understand why the Go authors chose the tradeoffs that they did.

Edit: Reread your comment and I get the part that is specific to interfaces now. This seems harder to exploit than, for example, a read of a slice header being... sliced... resulting in an out of bounds access.


Some languages guarantee memory safety even in the presence of data races (for example Java), while others simply prevent data races in their safe subset (rust). Go is normally memory safe but data races on some critical objects can compromise it.


Their egos have priced them out of a lot of stuff though.

The Chrome team is great but when you're surrounded by people who think they're god damn gods on Earth it's hard to question orthodoxies, especially old ones.


As trollish as that sounds, there's a painful amount of truth there.

Program in GNU Pascal or some such thing? Not us. Pedal to the metal!


It doesn't even have to be memory safe, just less memory accident prone.

D for example would have been an interesting choice (and I think it was usable back then).


D would be interesting but it's also not memory-safe. Not strictly. You can restrict yourself to SafeD, but then you're stuck with a GC you don't really want. Or you can tag all your functions as @Safe, but then it's just a "best practice" and you can't do it everywhere because you can't do things like make system calls.

D has some interesting bits, but it did a lot of head-scratching stuff too. It seemed really confused about what target it was going after, like some middle ground between C#/Java & C/C++ that really doesn't seem to exist.


D was released in 2001, KHTML in 1998, it's like suggesting they should have written vim in java.


... and Chrome in 2008.


Chrome wasn’t written from scratch in 2008. Chrome was a fork of WebKit, which was a fork of Konqueror, which was a fork of KHTML.


KHTML is the rendering engine for the Konqueror browser. Webkit is a fork of KHTML. And Chrome's engine is called Blink (it used to be Webkit, but they forked it).


C++, as that was a reasonable choice in <=2008. Today, perhaps new projects should pick something else (likely Rust).

Bits of Firefox are written in Rust, so Chrome could in theory under go the same "oxidation" (gradually replace the C++ code with Rust).

Edit: Clarify that Chrome was released in 2008 but was likely in development for some time before that.


> Chrome was released in 2008 but was likely in development for some time before that.

Well, sure, didn't it end up taking WebKit from Safari, which was C++, because it came from KDE Konqueror's KHTML?


Yes, Chrome was a fork.


Yes, the KHTML base for the rendering engine was started back in 1998, so C++ was a fine choice then. It's grown up as a code base significantly since then.


I'll be that guy. :)

Rust.

(I'm a front-end dev. I have no dog in this fight.)


It's funny how we define terms like "front-end" and assume a specific meaning. A web browser is certainly the front of something.


Rust.


Use-after-free is ruled out by e.g. Rust's type system


Nope. It's only true when all your code use safe subset/built upon a perfectly correct unsafe base (which is inevitable in a project like Chromium) with correct usage. Otherwise a single misused external call with unsanitized value can ruin all of your security guarantee.


Not really. It's been proved that if your unsafe foundations are correct and don't break the rules (i.e. their precondition is correct), then anything safe built on top of that is also correct.

Sure you could rely on a C/C++ library that causes problems, but then again you can either:

A) fix that library

B) replace that part with Rust or even stricter language.


There isn't a "safe or bug free" codebase in any language for any complex software project. The only code that you could possibly verify as "safe" are simplest of programs.

There is always a trade-off between complexity, security and performance.


if you think C or C++ programmers are ever going to admit this, you must never have met one


What is a C/C++ programmer? I've written fairly large amounts of C/C++ over the course of my career and I continue to do so. I totally concur with this and am eager to see Rust take its place over time.


I feel like I can call myself a "C++ programmer"; I was the lead for C++14 RTOS.

It's pretty hard to argue with the OP's logic.


I was mainly a C and C++ developer until 2006 and you will see me praise C++, but also be an heavy critic of the copy-paste security exploits it inherited from C.


> you can't write safe C++ in a codebase this complex.

And yet they did. And it worked great for more than a decade. Until just today when someone found...one.


A cursory look at Chrome security disclosures shows many memory corruption vulnerabilities.

The most recent release fixed twelve use after free vulnerabilities: https://chromereleases.googleblog.com/2019/01/stable-channel....

Go take a look at how many have been found in previous releases.


> According to the official release notes, this vulnerability involves a memory mismanagement bug in a part of Chrome called FileReader. That’s a programming tool that makes it easy for web developers to pop up menus and dialogs asking you to choose from a list of local files, for example when you want to pick a file to upload or an attachment to add to your webmail.

It sounds like the bug occurs interacting with external code, win32 or equivalent. Even if chrome were written in a "safe" language this section would likely be in unsafe block.


According to the patch, the problem involves memory management of DOM objects (specifically ArrayBuffers), not objects from native libraries.


> Let's just collectively admit it, finally - you can't write safe C++ in a codebase this complex.

And so, as they say, we should all be using Rust. But allow me to explain why I don't yet.

Rust has one really outstanding feature -- a borrower checker that makes it memory safe. Adding that to C++ would be a compatibility-breaking change. So then, while they were at it, they changed lots of other things. It's a whole different language, separate syntax, generics that work differently than templates, different standard library, etc.

This interacts with the finite amount of time I have to learn a new language, making it take longer before I feel proficient enough in it to start using it for real work. Sufficiently long that the day that I do is still in the future.

I understand the desire to make changes. Many of the changes are improvements, and C++ can be ugly. But it's the devil we know. A version of it with the minimum necessary changes to add a borrow checker would have me using it already, and I expect I am not the only such person.


> Rust has one really outstanding feature -- a borrower checker that makes it memory safe. Adding that to C++ would be a compatibility-breaking change. So then, while they were at it, they changed lots of other things. It's a whole different language, separate syntax, generics that work differently than templates, different standard library, etc.

> I understand the desire to make changes. Many of the changes are improvements, and C++ can be ugly. But it's the devil we know. A version of it with the minimum necessary changes to add a borrow checker would have me using it already, and I expect I am not the only such person.

What you say would make sense if Rust were based on C++. I've never heard anyone say that.

I was under the impression that Rust is a derivative of ML, and (to me, at least) it looks very much like StandardML or OCaml with a borrow checker.

(I don't know C++, so I can't personally comment on how similar or different it is from Rust)


It's sorta kinda both. The original implementation was in OCaml, and many of the early developers were big ML fans. But we also tried to keep syntax more C-like, overall.

"It's a C-like OCaml" was how Rust was originally described to me, when I first heard of it.


Moreover, it doesn't matter what it is based on, it matters what it is trying to displace.

I am not really even criticizing Rust. Let it be what it is. Python isn't C++ either. All I'm saying is that if the goal is to increase the number of people using memory safe languages, adding a borrow checker to C++ would serve that goal.

(Also, I would really like to have a language with both a borrower checker and actual C++ templates.)


What do you prefer about templates?

(And yes, I fully support making C++ safer too; the GSL is as close as we're going to get, probably.)


Template metaprogramming, variadic templates, integers as template parameters, etc. Even better would be constexpr user types as template parameters.

How about a language that embraces Turing-complete template metaprogramming as a thing to design in on purpose rather than something discovered after the fact to be more useful than anticipated. Have the utility without the ugly.


Cool, thanks! I think you'll be happy with Rust eventually; a lot of this is coming, likely this year. It's on the roadmap, though that has to be approved first. (The thing that isn't yet is variadrics, not sure when that'll happen.)

(And the reason it's taking such a while is that we are actually doing that...)


They are working on it. The analogue to the borrow checker in C++ is called the "lifetime profile checker" and (an incomplete version) is included in MS Visual C++, but last time I checked (in January) it seemed to still have too many false positives to be practical.

In the mean time, I think "the minimum necessary changes" to achieve memory and data race safety is to replace all your unsafe C++ elements (pointers, arrays, vectors, string_views, etc.) with compatible substitutes from the SaferCPlusPlus library [1]. You don't even need to replace them all at once. You can replace them incrementally and your code will continue to compile and run throughout the process. And where needed, maintain maximal performance as well [2].

[1] shameless plug: https://github.com/duneroadrunner/SaferCPlusPlus

[2] https://github.com/duneroadrunner/SaferCPlusPlus-BenchmarksG...


Different standard library is kind of unavoidable because you need standard library interface understood by borrow checker.


Microsoft already does that with their SAL annotations.

https://docs.microsoft.com/en-us/visualstudio/code-quality/u...

Which they are extending to also support their C++ lifetime checker.


That's the compatibility-breaking part. Doesn't mean the replacement function or object can't have the same name, purpose, time complexity, etc.

You could potentially even continue to support the existing ones but regard them as unsafe and have the compiler warn if they're used.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: