Advancing Memory Safety
security.googleblog.com
security.googleblog.com
> In addition, we are exploring more seamless interoperability with C++ through Carbon, as a means to accelerate even more of our transition to MSLs.
Carbon doesn’t really care about memory safety except as a vague “in the future we’ll evolve the language somehow to add more memory safety” statement. The primary goal seems to be to parse code as quickly as possible. So I’m not sure I understand how it improves the MSL story. Or is it saying that interfacing Carbon with a MSL like Rust is easier so carbon is a better bridge to MSL than things like autocxx?
Carbon is still a small team, so it is going to take time to achieve all of our goals. Carbon will need to demonstrate both C++ interop and memory safety to be considered a successful experiment, worth pushing to a 1.0 version. Once those are achieved, we do expect it will be easier to get C++ code to memory safety through Carbon, since that is the purpose it is being created for. The impedance mismatch between C++ and Carbon will be much lower than with Rust.
Parsing code quickly is merely one of those goals we can demonstrate today.
I remember a degree of pushback on FP, because the people that were into things such as Haskell and Scala were a little annoying with their desire to add FP everywhere too.
My takeaway is that annoying evangelists get a level pushback similar to how annoying they are.
So it's not only about C/C++.
Is that sarcasm? If not, thanks for proving my point.
From the top of my head, Go/.NET/Java are fast and safe choices. Also safe in the sense that you won't have a hard time hiring devs that are productive from the get go.
> proving my point.
Was your point misunderstanding that garbage collection != memory safety? Having garbage collection doesn't make a language any better for web dev. Being memory safe does. If you do not understand the difference and needlessly associate the two, you are validating the work put in by the evangelists.
What?! You really think memory safety is binary?
> Having garbage collection doesn't make a language any better for web dev.
It does though. It's more ergonomic than fighting rust compiler and does add safety over manual memory management from the likes of C/C++.
I just want to thank you for demonstrating so clearly how rust evangelists think.
Not sure why you'd assume that. The languages you offered are only as memory safe as their runtimes, which are usually implemented in something C flavored, and the compiler for same. Rust identifies memory unsafe areas instead with the unsafe tag. Functionally equivalent but I know which I like more.
> It does though. It's more ergonomic and does adds safety over manual memory management.
This is the misunderstanding. Rust manages to be wonderfully ergonomic without GC. The type system is really the only thing that makes it wordier than C++. And I've written large Rust applications with no manual memory management whatsoever. Occasionally I'll have to wrap something in an Arc Mutex because something needs to run in another thread. One or two variables in a whole application.
Your complaints apply to other languages.
I rest my case.
No amount of complaining about Rust evangelists will change how they use the language, but you could learn to understand why they're using it the way they are.
Thanks for the laugh. Maybe lifetimes/borrow checking syntax got invisible after a while to you. The only explanation.
I'll stop here. Don't have the time nor desire to argue against what seems like passionate opinions.
I just did a quick scan, in a couple thousand lines of Rust I see two, maybe three lifetime annotations. Nah, not a huge contributor to language verbosity. Most Rust folk seem to work with the compiler's notion of what lifetimes should be, and everything's implicit.
And op is correct. In terms of memory safety, Java and C# aren’t as safe as you might hope because significant chunks of the runtime are still in C/C++ (not true for Go) and because of JIT miscompilation bugs (also not relevant for go). Of course JIT miscompilation in theory should be the same as a compiler miscompilation, but in practice it turns out to be worse because you have to maintain consistency between the interpreted and all the tiers of JIT at runtime and in practice humans have a hard time doing that which is why V8 constantly has security vulnerabilities in this area. That’s why GraalVM is interesting because it proposes a JIT design where you only have the compiler implemented once.
Java has different compiler implementations not all of which necessarily go through interpreter stage. .NET CoreCLR does not have one in the first place (there is technically interpreter code in the codebase but it has bitrotted and is unused). JIT or not does not make it inherently more prone to miscompilation bugs. One thing that Go has going for its compiler is it is a massively simpler design. The downside of that, of course, is much worse compiler output that cannot cope with complex code and is inadequate at high-performance scenarios, where the users of Go either have to try to work around this with goasm or might not even have a solution at all, short of rewriting the component in a different language and praying that the FFI call overhead is low enough for the workload in question.
In addition to that, runtime implemented in C/C++ vs Java/C# or other language does not make it inherently safer. It is always a lower-level flavour of the language, where it cannot depend on itself, and with likely numerous relaxations in assurances to stay performant that the authors uphold manually. Even when C or C++ are used - it's usually a limited, focused subset of the language. And it is a bold claim that these runtimes are not extensively tested, fuzzed and otherwise verified against defects before going to production. If you look at bug and CVE history of .NET - pretty much all issues happen in complex "non-core" APIs, often at the unmanaged side provided by operating system rather than .NET itself (the cryptography bits). I can't recall any that are directly or indirectly related to a compiler bugs.
I feel like this reply ignores the wider context, which is plain old web dev, where the use of Rust and the decision fatigue that it carries is likely to be misplaced. Just pick C# or Kotlin.
I seriously doubt it. Naively incrementing Ref counters is extremely expensive. Tracing GCs in modern languages are very advanced. The least you could do is try to elide some of the ref increments - but you can't do that in Rust. Also boxing every object is very unpleasant. If you're gonna do that, use a language with reference semantics, like Java or C#
> significant chunks of the runtime are still in C/C++
Means absolutely nothing about the safety of the languages themselves. Just because C# has a runtime written in C++ does not mean that you can now magically compile and run use after frees or whatever.
That said, this is just my intuition here, I've never seen a good benchmark trying to do the comparison, so I don't want to over sell it either. But a lot of times, it's like, "okay I have four threads so I need the refcount to go to four" and then it's just using normal references to actually use the value inside.
Rc is cheap to the point of free unless it frees - it’s literally just an addition or subtraction with no data dependency in the path. Your CPU can do > 6 billion of these per second on a 6Ghz CPU (if I recall correctly you could do 3 per clock cycle in a micro benchmark due to out-of-order but at a minimum you should be able to do 1 per cycle).
Arc is quite expensive, particularly on x86 because of its memory model. You can maybe do a few million of them per second. But as you said, you shouldn’t be incrementing ownership once you’ve sent it to a new thread and instead using references directly or using something Rc-like to track the lifetime (i.e. when the current thread’s Rc count hits 0, release your Arc reference).
Runtime bugs are potentially exploitable, even from within the language implemented by the runtime. But I don't think anyone's suggested that they'd present as simply as you suggest. Many such bugs have been found in runtimes of many languages.
Compiler bugs can be similarly exploitable, with the caveat that any code eliminated at compile time won't be accessible at runtime, so compiled languages will tend to have a smaller surface area for exploitation in general.
As security experts like to repeat, it’s a numbers game. You only have to make a mistake once to result in an insecure system. And C++ encourages you to make those mistakes.
Same with JIT - because the compilation happens at runtime, a common attack mechanism is to exploit a particular bug in the JIT implementation so that the generated code violates some assumption and results in unsafe execution. This happens all the time with V8 and a large part of that problem comes from having to implement the language twice (once in the interpreter and once in the JIT and any deviation between the two implementations is an opportunity to exploit a vulnerability). This isn’t something I made up. This is from talking to people who are familiar with this and reading analyses by people who know what they’re talking about.
Compilers btw also have miscompilation bugs all over the place. The reason it’s less of a problem is because the code being executed isn’t “malicious” - it’s your code and you’re not going out of your way to structure code to find bugs in the compiler. This also protects most JIT and why most C# programs don’t have this problem - they’re not loading arbitrary code at runtime to execute. It is a particular problem for V8 though where that’s an expected behavior.
What argument? Seems more like a misunderstanding to me.
In fact, that has broken Rust compilation semantics quite a few times already.
I can say that Rust is well architected such that the borrow checker and most of memory safety is implemented in the midlevel intermediate representation, which is implemented using Rust, and the bits of Clang upon which MIR and HLR are built are both well tested by virtue of being used by multiple language frontends, and limited in scope and modularity. It'd be great to see an all-rust replacement though, as bugs in Clang have been found. I believe there are several projects in the works and existing pure-Rust bootstrap compilers sufficient to get the toolchain working.
Hahahahaha! That's funny.
Not the only one. Maybe the fastest, yes. But... at what cost?
Also, I think people seem to believe that memory safety is all safeties. Attacks come in many shapes and forms, from supply chain injection, to SQL injection, to unsanitized input, overflows, secret leaking... memory safety is one of the weak points of a chain, but not the only one.
All require runtimes. Which means they're not in the same class of language as C, C++, and Rust.
> Also, I think people seem to believe that memory safety is all safeties.
It's 70% of CVEs in C and C++. People focus on it because it's the biggest single category. Enums and Option types and the whole type system for that matter goes a long way toward addressing logic bugs in Rust as well.
Right, which is why it makes no sense to say "no don't use C# for webdev!! Use Rust!"
They're different classes of languages. If C# is good for that use case then use it. Also there are genuine benefits to having a runtime.
Well, first of all, I don't see anywhere where anyone said that. Perhaps in another thread? People did argue against the use of Rust for web dev, for which it's a perfectly reasonable choice among many.
Second, I have an embedded HTTP server running on a microcontroller with 500kb of ram in my current project. Works great in Rust. Simply not possible in C#.
> If [any language] is good for that use case then use it.
Agreed. The best camera is the one in your pocket, and the best language is the one you know.
> Also there are genuine benefits to having a runtime.
Things which are commonly associated with runtimes - memory safety, automatic memory management, various type systems, high level language abstractions - are all genuinely useful. Folks in this thread seem to want to hold on to a categorization of languages which Rust breaks by virtue of providing features typically only found in languages with runtimes. What Rust shows is that those benefits can be a part of a well designed language, with or without a runtime. And not having a runtime is a hard requirement in areas like embedded, OS development, realtime, etc. There is typically a separation between languages you'd use for that sort of thing and languages you might use for web dev, but the reasons for that separation do not apply to Rust. Which is fun.
Some people seem to be salty about it though, lol.
One significant downside to having a runtime is that it significantly complicates the process of program verification. Making it much harder to do statically. Necessitating certain things happen at runtime like error handling and performance profiling. Rust mostly has what it needs to get that work done at compile time, which means the developer has an opportunity to fix it, before some user runs into it.
This is the point - no, you actually DON'T get all the benefits of a runtime. C# famously, and extensively, used runtime code generation. Not possible in Rust, kind of possible in C++.
> Necessitating certain things happen at runtime
Not how it works. The language is still compiled. You can 100% implement a borrow checker for C#, there's nothing stopping anyone. It's just not what they want to do.
For example, C++ and C are BOTH compiled languages. But C++ is able to verify the type of various operations at compile-time, but C has to wait until runtime. For example, C's qsort takes void pointers and then you cast and dereference them at runtime. In C++, this is physically baked into the type of function template std::sort.
But they're both compiled languages with no runtime. But because C++ has a much more complete type system, it can do those things at compile-time while C cannot.
Another example: while C# has polymorphized generics, it checks them at compile time! The generics are not monomorphized like C++ or Rust, which allows smaller linked libraries that you can load and use at runtime, which is not possible in C++ templates or Rust generics. The trade-off is then that generics can't be checked until runtime - but actually no. The C# compiler will go out of its way to check generics when it can and will fail if they don't meet the type constraints, just like C++ or Rust.
Rust is very powerful, BUT:
1. It doesn't cover all the usecases of more powerful languages with rich runtimes like C# or Java
2. Using async rust, which is typically a requirement for webdev, requires a runtime anyway.
Generics are not monomorphized at bytecode level, but whenever generic argument is a struct, the methods and types that have it are monomorphized at the stage of compiling to machine code, be it with JIT or ILC. This is identical to Rust, and is why C# has "zero-cost" abstractions.
Method bodies and types for class-type generic arguments are indeed shared however. Dispatch on generic constraints is still quite efficient in that case, even if no longer zero-cost (unless the compiler is able to see the exact type and inline such calls, or emit a guarded devirtualized fast-path when compiled by JIT, or prove that only few types implement a particular interface or subclass a specific parent when compiled by NativeAOT).
The only big problem I see with Rust that makes it a bad fit for webdev is the young ecosystem. The compile times are not great, but I don't think that's a significant issue when writing APIs.
However, There here are some immensely annoying people (just see other replies here) that can't fucking shut up about how their screwdriver of choice is better.
Except it is not.
As any other language, it has many potential upsides and downsides in picking it depending on the context.
This lack of nuanced thinking from evangelists is precisely why some people just prefer to shut it off entirely.
I am saying that depending on the context I might pick Go. Or Java. Or Python. Or Node. Or C#.
For example, let's say Rust is the best language in the world, provably safe.
1. it is in practice? You will use unsafe somewhere: C interfaces, for example, or some abstractions.
2. even if it is still theoretically safer: can I finish my work with it? Ecosystem, libraries, etc.
3. do I have trained people for it?
4. what is the cost of writing software in it compared to other languages?
5. is my system really, really safety-critical? What are the consequences of my program not working well?
If it was really that easy, all of us would code proofs with something like this: https://dafny.org/ and would use languages like Rust.That is not the case at all and there are a ton of variables, from which cost, and I mean short vs long term cost where short term or middle term dominates for which it makes just more sense to choose the "worse" tool. Why? Because the "worse" tool will make that piece of software exist. The "better" one will probably make it never exist, because of other costs.
Whoever ignores this when doing anything, from coding to other activities, then that's ignoring reality. If human lives are at stake, yes, increase the cost a lot, make it the safest you can, but, even in that case, probably achieving 100% is impossible, or maybe achieving 99.999999% is 10 times cheaper than achieving 99.99999999999999999%.
By this measure, we would not have a lot of the imperfect inventions that improved over time in many areas.
And if the people advocating it are crazy, you might start to wonder if they're telling the whole truth!
This really applies to everything IMO.
While I personally don't like people promoting niche concepts as well (I'd rather have them promote building out a solid ecosystem as you say), I still feel the "brain drain" out of the Haskell community.
* We now have HLS, which integrates superbly with VS Code (it's also fine in Emacs and I guess in other editors, but I don't know). It can be flaky on large codebases, but it's a big step up from what we had before (which was almost nothing).
* GHC and bundled libraries have a much higher degree of API stability. In fact in the last 18 months there has been minimal breakage due to new GHC releases[1]. Community libraries are a bit more of a mixed bag, but there's much more awareness in the community that continual API breakage churn is harmful for adoption.
* We now have the Haskell Foundation[2] supporting all sorts of critical ecosystem activity behind the scenes, and helping people work together harmoniously. There used to be extremely fraught and unpleasant interpersonal battles in the community. Those are a thing of the past.
* We now have solid effect systems (Bluefin[3] and effectful) that combine the best of the transformers world (build effects from components, type safety, handle effects and remove them from the type) and the IO-based world (predictable performance, resource safety, escape hatches). I'm willing to boldly claim that effects are no longer a problem in Haskell.
I'm in the same boat as you: I don't use particularly man new libraries. Is that a problem? I'm given to understand that in the Java world that's called "Monday". Sure, I would have liked it if certain community members had stayed more active, but I don't feel I'm missing anything in particular. (That may just be to do with what I work on.) What are you missing?
[1] See
* https://github.com/tomjaguarpaw/tilapia/blob/master/breakage...
* https://github.com/tomjaguarpaw/tilapia/blob/master/breakage...
[2] Disclaimer: I'm the vice chair
[3] Disclaimer: I'm the effect system author
Rust is much easier to recommend than Scala ever was. Rust's ecosystem is a bit smaller than you'd typically like, but that's a temporary problem.
I would say that what is bad about this is like they seem to think there is no other way. There is "the true Rust way" and after that, the others. At least I got that attitude, because when claims have been done about why the only path to Safe C++ is to copy Rust and I question it, they reply as "what you say is impossible" and I show them there are ways, then they move the attack somewhere else, even I have been accused of "wasting their time"...
Absolutely. Swift was hated because it lacked so much Objective-C functionality at launch, Kotlin got a similar "Java is fine" reaction, and Typescript got a "why add more complexity we don't need" reaction.
This is just the way of things. The most hated languages are some of the most used ones (Javascript, PHP, Python, etc).
(Applies to much more than just languages!)
There are two types of people. The people that like Van Morrison and the people that have met him
Mandatory quote: "There are only two kinds of languages: the ones people complain about and the ones nobody uses". -- Bjarne Stroustrup
To be fair, those were totally valid criticisms before HotSpot got really good. IIRC, way back in the day Corel said they were going all in on Java for their software suite, and they eventually abandoned it due to performance.
But I’d also spent a lot of time thinking about performance already before finding Java. A lot of people fully embraced the abstract nature of it while I kept it at arm’s length.
I fixed a lot of really dumb mistakes, and a lot of things that were subtly wrong but with large consequences.
* JIT was added in Java 1.1 for Windows, and 1.2 for everyone else. But in the early days it sucked.
* Generics, autoboxing, and enums were added in Java [1.]5 (2004). Prior to that, APIs had to use `Object` everywhere, explicitly wrap primitives when needed, and use plain ints for constants.
* It's $CURRENTYEAR and Java still doesn't support structs, and it's not clear they actually understand the problem (since e.g. they force everything to be `final`).
I think they do. The problem they are solving is removing indirection and flattening the memory representation of data. Making all the fields final opens up the JVM to some really neat optimizations you can't get with mutable fields.
By the time you get something with enough fields in it that the immutability limitation becomes problematic, you likely would benefit from just having a regular class. Structs won't always be strictly faster than regular classes.
they call it records, added recently. Depending on where you work, you may see them in a decade
You will see `value record Point(int x, int y)` which is different from `record Point(int x, int y)`. The key difference being that `record Point` will create a reference to the x/y data with identity whereas `value record Point` will instead represent that data inline without a reference.
I suspect that `value record` will be the way pretty much everyone starts using `record` as there's basically no reason not to.
You can play with early releases, https://jdk.java.net/valhalla/
Not because it's the worst, but because more people use it, and because it's the one people have to use even though they don't like it.
We just need to admit that we suck at it.
And I have that C/C++ bias: I don't enjoy programming in rust as much as I do C, but I don't like doing lots of new things.
Many languages expelled C/C++ from the domains they were once kings of entirely. It used to be "the language" for many, many things.
Saying C/C++ denotes your poor understanding of C and C++, which are usually quite different in style and with very different safety characteristics regarding to code written in good style.
But much as there's an indignity to something compiling to JS or your own preferred VM, people were convinced that memory allocation/GC is the reason they got a job and tended to object accordingly - so there was fight against that. Rust is treated by the hard-core C-ists as an affront to their ability to manage memory safely. Go look up early go vs C (or C#, D) - it had its own animated themes, but this was before GG went fucky with its own open source stuff so there was an air of 'google is trustworthy' in those discussions.
TLDR: Yes.
And yes there has been enough anti-GC hate and FUD from those that think all automatic resource management systems are born alike.
I recall working with greybeard types who found ARC to be a little sugary and pined for the days of NSAutoReleasePool. I think this is just a natural cycle of aging builders and their tools, I imagine it predates us all.
These days I pretty frequently find myself frustrated with SwiftUI and wishing I'd just stuck with UIKit, but alas, time waits for no man.
Also, no other language has really posed an existential threat to C/C++ developers. Other languages have narrowed the pool, but Rust is the first to really be a possible replacement. Now, there's no real chance that Rust will actually replace C/C++ in the near future, but even the threat can be a huge concern.
I think the problem is that other variables (not only safety) must be assessed beyond the pure "better". Haskell is very good also. Very correct. Who uses that, and where? And why? Why not everyone uses https://dafny.org/ ?
Small problems are immediatly obvious in these sorts of replies. Hard to use off the shelf modules if they don’t all use the same memory management replacements. To be fair third party code by volume was in languages like Python or Java. Hard to miss what you haven’t experienced firsthand.
The problem here is that is doesn't work with Rust.
Also I read a story of a developer resisting move from hand-written assemby to C, to which company owner replied "you can write your assembly, but not for my money". So nothing new, though some languages get a lot more resistance than others.
Absolutely and STILL TO THIS DAY.
I code on my free time because I enjoy doing so. I find C to be fun (even if I suck at it)
Doing boring thing (or things I do not want to) is called work
1) It's very assertive without having a lot of experience or a track record.
2) It's extremely online, filled with anime references and Twitter jokes, obsessed with cuteness, etc...
3) It's full of whipper-snappers with 6 months of experience who think they can explain your job to you, who have been doing it for 30 years.
IME many arguments around Rust aren't focused on its technical properties, but boil down to "I just plain don't like you people."
If Rust can successfully become "boring", a lot of the opposition will go away.
With all good and bad things this entails.
It seems there is a push from some people (that I hope it is not successful in its current shape, I personally find it a wrong approach) to just bifurcate C++ type system and standard library.
More context here if you are curious: https://www.reddit.com/r/cpp/comments/1g41lhi/memory_safety_...
What I found a lot of is literally, intellectual dishonesty in some of the arguments to push in that direction.
Not that it is not a possible direction, but claims like: "there is no other way", "it is the only feasible solution", etc. without exploring other alternative, from which Swift/Hylo value semantics/subscripts (subscripts is basically controlled references for mutation without a full borrow checker) and adding compile-time enforced law of exclusivity semantics without a new type system are possible (and better, IMHO) alternatives... well, you can see the thread.
There seems to be a lot of obsession as "the only right way is the Rust way" and everything else is directly discardable by authors and some people supporting them.
I think there are a lot of strong feelings there.
It's so fucking safe...
Great examples:
- Reference to mut static (interacting with it is already unsafe), but now you get additional warnings.
- the entire addr_of!() mess.
Things like these just make me wish I did this in c where I know I am only ever executing in a single thread.