10 ms·
Eliminating Memory Safety Vulnerabilities at the Source
- steveklabnik 2y agoThis is a very interesting post! One takeaway is that you don't need to re-write the world. Transitioning new development to a memory safe language can bring meaningful improvements. This is much easier (and cheaper) than needing to port everything over in order to get an effect.
- gary_0 2y agoIn fact, these results imply that the benefits of re-writing the world are limited in terms of security. This raises the cost-benefit ratio of keeping mature legacy code and only using memory-safe languages for new code. This also implies that languages and tooling with robust support for integrating with unsafe legacy code are even more desirable.
- Xylakant 2y agoEssentially, this has been Rusts value proposition from the outset - build a language that you can integrate into other codebases seamlessly, hence the choice of no runtime, no garbage collector etc. Bindgen (https://github.com/rust-lang/rust-bindgen https://github.com/rust-lang/rust-bindgen) and similar tooling were around essentially since day one to assist in that. It’s the only approach that has any chance of transitioning away from unsafe languages for existing, mature codebases. Rewriting entirely in a different language is not a reasonable proposition for every practical real-world project.
- dadrian 2y agoRust had to be dragged kicking and screaming into integration with other languages, and its C++ compatibility is a joke compared to Swift. It's absolutely true that you need integration and compatibility to enable iterative improvements, but Rust historically has been hostile to anything besides unsafe C ABI FFI, which is not suitable for the vast majority of incremental development that needs to happen. Luckily, this is starting to change.
- Philpax 2y ago"Hostile" is assigning intent that was not present. C++ ABI integration was and is extremely difficult; you need to be able to fully handle C++ semantics and to target each platform's take on the ABI. Most C++ competitors have struggled with this for much the same reason. This means that "solving this" requires partial integration of a C++ compiler; it's not a coincidence that the languages with the most success have been backed by the organisations that already had their own C++ compiler. A much easier solution is to generate glue on both sides, which is what `cxx` does.
- bluGill 2y agoIf the intent was to interoyerate with c++ then not supporting the api is hostile. I have a lot of code using vector, if I have to convert that to arrays then you are hostile and worse that is new code which per the article makes it suspect d does support c++ abi. It seems almost dead now but it is possible. realistically there are two c++ abis in the world. Itanimum and msvc. both are known well enough that you can imblement them if you want (it is tricky)
- Philpax 2y ago> If the intent was to interoyerate with c++ then not supporting the api is hostile. I have a lot of code using vector, if I have to convert that to arrays then you are hostile and worse that is new code which per the article makes it suspect It's not hostile to not commit resources to integrating with another language. It might be shortsighted, though. > d does support c++ abi. It seems almost dead now but it is possible. That was made possible by Digital Mars's existing C++ compiler and their ability to integrate with it / borrow from it. Rust can't take the same path. Additionally, D's object model is closer to C++ than Rust's is; it's not a 1:1 map, but the task is still somewhat easier. (My D days are approaching a decade ago, so I'm not sure what the current state of affairs is.) > realistically there are two c++ abis in the world. Itanimum and msvc. both are known well enough that you can imblement them if you want (it is tricky) The raw ABI is doable, yeah - but how do you account for the differences in how the languages work? As a simple example - C++ has copy constructors, Rust doesn't. Rust has guarantees around lifetimes, C++ doesn't. What does that look like from a binding perspective? How do you expose that in a way that's amenable to both languages? `cxx`'s approach is to generate lowest-common denominator code that both languages can agree upon. That wouldn't work as a language feature because it requires buy-in from the other side, too.
- rurban 2y agoWhy is Rust then insecure by default, with exicit unsafe blocks leading to type and memory unsafeties, plus blocking IO leading to concurrency unsafeties? They are talking about actually safe languages, secury by design and secure by default. Which would be a lisp or scheme without an FFI, or a beam language (Erlang, Elixir). Or pony, without the FFI. Or Concurrent Pascal, not Go. Or a safe scripting language
- dieortin 2y agoThe article explicitly uses Rust as an example of a safe language
- Ygg2 2y agoFrom security perspective I agree but what if you want to be rid of GC or just reduce your overall resource consumption?
- steveklabnik 2y agoThen you should use the language that’s memory safe without GC.
- Ygg2 2y agoYes, I'm saying in those cases it does make some sense to rewrite it in Rust.
- UncleMeat 2y agoIf you have a codebase that has no security implication and won't receive significant benefit from improved productivity/stability from a more modern language, almost certainly not. Such projects exist. That's fine.
- nine_k 2y agoYour engineers are usually your most expensive resource. Developing software in Typescript or even Ruby is a way to get to the revenue faster, having spent less money on development. Development cost and time (that is, the opportunity cost) are usually the most important limitations for a project where the defect rate does not need to be extremely low (like in aircraft control firmware). Rust saves you development time because less of it is spent fixing bugs, but often would pick it not because it saves you RAM and CPU cycles; Haskell or, well, Ada/Spark may be comparably efficient if you can wield them.
- hyperman1 2y agoThis is true, but there is a crossover point where engineers spend more time understanding existing code than writing new code. Crossing it is typically a point where more static languages with more built in checks become cheaper than more dynamic code. In my experience, it takes about a year to reach this point, less if you hire more people.
- pjc50 2y agoRewrites are so expensive that they're going to be very rare. But incremental nibbling at the edges is very effective. I wonder if there's a "bathtub curve" effect for very old code. I remember when a particularly serious openssl vulnerability (heartbleed?) caused a lot of people to look at the code and recoil in horror at it.
- WuxiFingerHold 2y agoI don't think it is in itself that surprising, that writing all new code in MSLs leads to less MS vulnerabilities. The likelihood of new code introducing new MS issues is after all higher than to discover existing ones in old code (which still happens of course). However, it is impressive that from 2019 to 2023 the issues went down by almost 60% without rewriting, just by adding new code in MSLs (Rust mostly, I guess, probably a bit Kotlin). 60% is a massive achievement. I wonder why there's a plateau from 2021 and 2023. The fact that they use extrapolated number for 2024 reveals their intention to show nice numbers, but it would have been more beneficial if they'd have spent the time to analyse and explain the rise from 2022 to 2023. I'm a bit disappointed that they don't tell us how much of the new MS code is written in which language. Seems like they're using both Rust and Kotlin. Is it 95% Rust? Or "just" 50% Rust? If the Kotlin portion is significant, in what areas do they use it instead of Rust?
- cakoose 2y agoWhat happens if we gradually transition to memory-safe languages for new features, while leaving existing code mostly untouched except for bug fixes? ... In the final year of our simulation, despite the growth in memory-unsafe code, the number of memory safety vulnerabilities drops significantly, a seemingly counterintuitive result [...] Why would this be counterintuitive? If you're only touching the memory-unsafe code to fix bugs, it seems obviously that the number of memory-safety bugs will go down. Am I missing something?
- cogman10 2y agoThe counter intuitive part is that there is now more code written in memory unsafe languages than there was before. Even if it's just bug fixing. It's not as if bug fixes haven't resulted in new memory bugs, but apparently that rate is much lower in bug fixes than it is in brand new code.
- MBCook 2y agoI think the standard assumption would be that you need to start replacing older code with memory safe code to see improvements. Instead they’ve shown that only using memory safe languages for new code is enough for the total bug count to drop.
- Animats 2y agoHalf a century since Pascal. Forty years since Ada. 28 years since Java. Fifteen years since Go. Ten years since Rust. And still unsafe code is in the majority.
- mpweiher 2y agoWell, that tells us something...
- tiffanyh 2y agoDoesn't libc interface requirement of C/C++ use, create massive downstream challenges for other languages to gain super mass adoption (at the OS level).
- sanxiyn 2y agoYes, but Linux (hence Android) doesn't have that problem, because its interface is system call not libc.
- tiffanyh 2y agoBut don't syscalls require C/C++ data structures & type definitions. So while not technically "requiring" C/C++, if your language cannot map exactly to C/C++ data structs & type definitions - it won't work.
- nineteen999 2y agoYes.That's a problem for the contenders, Linux/UNIX kernel are written in C. Unless we want to add language-specific syscall interfaces for every compiled language out there. Alternative is to, y'know, write a kernel in your language of choice and choose your own syscall specification suiting that language, and gain mass adoption. Easy!
- saagarjha 2y agoThe practical interface for Android is Binder, which has an interface that can be made amendable to a richer language.
- SkyMarshal 2y agoThey talk about "memory safe languages (MSL)" plural, as if there is more than one, but only explicitly name Rust as the MSL they're transitioning to and improving interoperability with. They also mention Kotlin in the context of improving Rust<>Kotlin interop, which also has some memory-safe features but maybe not to same extent as Rust. Are those the only two Google uses, or are there others they could be referring to?
- steveklabnik 2y agoA few thoughts: People who care about this issue, especially in the last few years, have been leaning into a "memory safe language" vs "non memory safe language" framing. This is because it gets at the root of the issue, which is safe by default vs not safe by default. It tries to avoid pointing fingers at, or giving recommendations for, particular languages, by instead putting the focus on the root cause. In the specific case of Android, the subject of this post, I'm not aware of attempts to move into other MSLs than those. But I also don't follow Android development generally, but I do follow these posts pretty closely, and I don't remember any of them talking about stuff other than Rust or Kotlin.
- amluto 2y ago> I don't remember any of them talking about stuff other than Rust or Kotlin. Don’t forget the old, boring one: Java. I assume the reason that Go doesn’t show up so much is that most Android processes have their managed, GC’d Java-ish-virtual-machine world and their native C/C++ world. Kotlin fits in with the former and Rust fits in with the latter. Go is somewhat of its own thing.
- vvanders 2y agoAndroid has a surprising amount of core OS functionality in boring managed Java code. ART/Dalvik are quite impressive combined with a few other clever tricks to make a system that ran in a pretty small footprint.
- 2y ago
- daft_pink 2y agoI’m curious how this applies to Mac vs Windows, where most newer Mac code is written in memory safe swift, while Windows still uses primarily uses C or C++.
- akyuu 2y agoApple is still adding large amounts of new Objective-C code in each new macOS version [0]. I haven't found any language usage numbers for recent versions of Windows, but Microsoft is using Rust for both new development and rewriting old features [1] [2]. [0] Refer to section "Evolution of the programming languages" https://blog.timac.org/2023/1128-state-of-appkit-catalyst-swift-swiftui-mac/ https://blog.timac.org/2023/1128-state-of-appkit-catalyst-sw... [1] https://www.theregister.com/2023/04/27/microsoft_windows_rust/ https://www.theregister.com/2023/04/27/microsoft_windows_rus... [2] https://www.theregister.com/2024/01/31/microsoft_seeks_rust_developers/ https://www.theregister.com/2024/01/31/microsoft_seeks_rust_...
- TazeTSchnitzel 2y agoIt should be noted that Objective-C code is presumably a lot less prone to memory safety issues than C code on average, especially since Apple introduced Automatic Reference Counting (ARC). For example: • Use-after-frees are avoided by ARC • Null pointer dereferences are usually safe (sending a message to nil returns nil) • Objective-C has a great standard library (Foundation) with safe collections among many other things; most of C's dangerous parts are easily avoided in idiomatic Objective-C code that isn't performance-critical But a good part of Apple's Objective-C code is probably there for implementing the underlying runtime, and that's difficult to get right.
- saagarjha 2y agoMost of Apple's Objective-C code is in the application layer just like yours is
- daft_pink 2y agoI found the evolution of programming languages article you sourced very interesting. Just to summarize the article, it shows that writing completely new code in memory safe language, while maintaining non-memory safe code, results in a steep reduction in memory safe errors overtime even though it results in an overall increase in unsafe code. It says that most memory safe vulnerabilities come from completely new code not maintained code and thus argues you can get the most of the benefits of memory safe code without rewriting your entire code base, which I think is the main takeaway from the article. I’m not sure that’s totally happening in MacOS from reading your article, but it kind of is, so I think my hypothesis is correct that MacOS will likely have less vulnerabilities as it transitions many newer projects to swift although its important to note that important vulnerable projects such as webkit are still written in C++.
- ievans 2y agoSo the argument is because the vulnerability lifetime is exponentially distributed, focusing on secure defaults like memory safety in new code is disproportionately valuable, both theoretically and now evidentially seen over six years on the Android codebase. Amazing, I've never seen this argument used to support shift/left secure guardrails but it's great. Especially for those with larger, legacy codebases who might otherwise say "why bother, we're never going to benefit from memory-safety on our 100M lines of C++." I think it also implies any lightweight vulnerability detection has disproportionate benefit -- even if it was to only look at new code & dependencies vs the backlog.
- xyst 2y ago[flagged]
- wepple 2y agoWe love it. Can move on to the other two hundred problems to work on. Including where CNE will go next; logic and web bugs.
- dataking 2y agowell put! making sure that the new, memory safe code interoperates with the old code and is equally well supported by tooling takes an incredible amount of work.
- deleted 2y ago[deleted]
- Wowfunhappy 2y ago> The answer lies in an important observation: vulnerabilities decay exponentially. They have a half-life. [...] A large-scale study of vulnerability lifetimes2 published in 2022 in Usenix Security confirmed this phenomenon. Researchers found that the vast majority of vulnerabilities reside in new or recently modified code. It stands to reason, then, that it would be even better for security to stop adding new features when they aren't absolutely necessary. Windows LTSC is presumably the most secure version of Windows.
- wepple 2y agoOr an alternative approach: only compile the subset of features you explicitly need. Obviously there’s a ton of variance in how practical this is any place, but it’s less common than it should be.
- sieabahlpark 2y agoAllow me to introduce a whole new suite of bugs that occur when feature A exists but feature B doesn't. Congrats you're back to square 1!
- wolrah 2y ago> Allow me to introduce a whole new suite of bugs that occur when feature A exists but feature B doesn't. Yeah, but are those bugs security bugs? Memory safety bugs are a big focus because they're the most common kind of bugs that can be exploited in a meaningful way. Disabling entire segments of code is unlikely to introduce new memory safety bugs. It's certainly likely to find race conditions, and those can sometimes lead to security bugs, but its not nearly as likely as with memory safety bugs.
- ReleaseCandidat 2y ago> Yeah, but are those bugs security bugs? If the software is unusable, it doesn't matter if it has security bugs too. Or, to rephrase, the safest software is software nobody uses.
- naming_the_user 2y agoI'm a little uneasy about the conclusions being drawn here as the obvious counterpoint isn't being raised - what if older code isn't being looked at as hard and therefore vulnerabilities aren't being discovered? It's far more common to look at recent commit logs than it is to look at some library that hasn't changed for 20 years.
- SoylentOrange 2y agoI don’t understand this point. The project under scrutiny is Android and people are detecting vulnerabilities both manually and automatically based on source code/binary, not over commit logs. Why would the commit logs be relevant at all to finding bugs? The commits are just used for attribution. If there was some old lib that hasn’t been changed in 20 years that’s passed fuzzing and manual code inspection for 20 years without updates, chances are it’s solid.
- saagarjha 2y agoExploit authors look at commit logs because new features have bugs in them, and it's easier to follow that to find vulnerabilities than dive into the codebase to find what's already there.
- e28eta 2y agoI wasn’t entirely satisfied with the assertion that older code has fewer vulnerabilities either. It feels like there could be explanations other than age for the discrepancy. For example: maybe the engineers over the last several years have focused on rewriting the riskiest parts in a MSL, and were less likely to change the lower risk old code. Or… maybe there was a process or personnel change that led to more defects. With that said, it does seem plausible to me that any given bug has a probability of detection per unit of time, and as time passes fewer defects remain to be found. And as long as your maintainers fix more vulnerabilities than they introduce, sure, older code will have fewer and the ones that remain are probably hard to find.
- MBCook 2y ago> what if older code isn't being looked at as hard and therefore vulnerabilities aren't being discovered? It wasn’t being look at as hard before either. I don’t think that’s changed. They don’t give a theory for why older code has fewer bugs, but I’ve got one: they’ve been found. If we assumed that any piece of code has a fixed amount of unknown bugs per 1000 lines, it stands to reason that overtime the sheer number of times the code is run with different inputs in prod makes it more and more likely they will be discovered. Between fixing them and the code reviews while fixing them the hope would be that on average things are being made better. So overtime, there are fewer bugs per thousand lines in existing code. It’s been battle tested. As the post says, if you continue introducing new bugs at the same rate you’re not going to make progress. But if using a memory safe language means you’re introducing fewer bugs in new features then overtime the total number of bugs should be going down.
- benwilber0 2y ago> Increasing productivity: Safe Coding improves code correctness and developer productivity by shifting bug finding further left, before the code is even checked in. We see this shift showing up in important metrics such as rollback rates (emergency code revert due to an unanticipated bug). > The Android team has observed that the rollback rate of Rust changes is less than half that of C++. I've been writing high-scale production code in one language or another for 20 years. But I when I found Rust in 2016 I knew that this was the one. I was going to double-down on this. I got Klabnik and Carol's book literally the same day. Still have my dead-tree copy. It's honestly re-invigorated my love for programming.
- pclmulqdq 2y agoThat makes sense because the #1 reason I have had to roll back my own C++ commits is due to crashes from some dumb failure to check whether a null pointer is null. If Rust is going to prevent that issue and other similar issues of stupid coding, you would expect whole classes of rollbacks to go away.
- acdha 2y agoI now compare C to an IKEA bed we have in our guest room which has storage drawers making the edge straight down to the floor without a gap. I’m a grownup, I know that I need to stop half a step early, but every few weeks I stub a toe while I’m thinking about something else.
- ahoka 2y agoTBH most of these issues go away when your language has no implicit nullability.
- steveklabnik 2y agoThat’s very kind, thank you.
- selfmodruntime 2y agoI feel entirely the same. I actively miss Rust when I need to choose another language.
- 0xbadcafebee 2y agoSo there's a C program. There's a bunch of sub-par programmers, who don't use the old, well documented, stable, memory-safe functions and techniques. And they write code with memory safety bugs. They are eventually forced to transition to a new language, which makes the memory safety bugs moot. Without addressing the fact that they're still sub-par, or why they were to begin with, why they didn't use the memory safe functions, why we let them ship code to begin with. They go on to make more sub-par code, with more avoidable security errors. They're just not memory safety related anymore. And the hackers shift their focus to attack a different way. Meanwhile, nobody talks about the pink elephant in the room. That we were, and still are, completely fine with people writing code that is shitty. That we allow people to continuously use the wrong methods, which lead to completely avoidable security holes. Security holes like the injection attacks, which make up 40% of all CVEs now, when memory safety only makes up 25%. Could we have focused on a default solution for the bigger class of security holes? Yes. Did we? No. Why? Because none of this is about security. Programmers just like new toys to play with. Security is a red herring being used to justify the continuation of allowing people to write shitty code, and play with new toys. Security will continue to be bad, because we are not addressing the way we write software. Rather than this one big class of bugs, we will just have the million smaller ones to deal with. And it'll actually get harder to deal with it all, because we won't have the "memory safety" bogey man to point at anymore.
- tptacek 2y agoNo, this just isn't how it works. You'll find business logic, domain-specific, and systems programming security errors on every platform, but you don't find them with the density and the automatic severity you do memory safety issues. This is not about "language hipsters".
- 0xbadcafebee 2y agoIf the impetus behind moving to new languages was primarily focused on improving security, we could have first attempted to address the underlying cause of programming security issues. The underlying cause is not "it's hard to check buffer boundaries". If you don't believe me, do a 5 Whys on the average buffer overflow, and tell me the best solution is still a new language.
- Stem0037 2y agoThe idea of "Safe Coding" as a fundamental shift in security approach is intriguing. I'd be interested in hearing more about how this is implemented in practice.
- alpire 2y agoFor more information on our safe coding approach, as applied to the web domain, check out this paper (https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/42934.pdf https://static.googleusercontent.com/media/research.google.c...) or this talk (https://www.youtube.com/watch?v=ccfEu-Jj0as https://www.youtube.com/watch?v=ccfEu-Jj0as).
- infogulch 2y agoI'd like to acknowledge that the charts in this article are remarkably clear and concise. A great demonstration of how careful data selection and labeling can communicate the intended ideas so effortlessly that they virtually disappear into the prose. So the upshot of the fact that vulnerabilities decay exponentially is that the focus should be on net-new code. And spending effort on vast indiscriminate RiiR projects is a poor use of resources, even for advancing the goal of maximal memory safety. The fact that the easiest strategy, and the strategy recommended by all pragmatic rust experts, is actually also the best strategy to minimize memory vulnerabilities according to the data is notably convergent if not fortuitous. > The Android team has observed that the rollback rate of Rust changes is less than half that of C++. Wow!
- 0xDEAFBEAD 2y agoTrying to think through the endgame here -- As vulnerabilities become rarer, they get more valuable. The remaining vulnerabilities will be jealously hoarded by state actors, and used sparingly on high-value targets. So if this blog post describes the 4th generation, perhaps the 5th generation looks something like Lockdown Mode for iOS. Let users who are concerned with security check a box that improves their security, in exchange for decreased performance. The ideal checkbox detects and captures any attack, perhaps through some sort of virtualization, then sends it to the security team for analysis. This creates deterrence for the attacker. They don't want to burn a scarce vulnerability if the user happens to have that security box checked. And many high-value targets will check the box. Herd immunity, but for software vulnerabilities instead of biological pathogens. Security-aware users will also tend to be privacy-aware. So instead of passively phoning home for all user activity, give the user an alert if an attack was detected. Show them a few KB of anomalous network activity or whatever, which should be sufficient for a security team to reconstruct the attack. Get the user to sign off before that data gets shared.
- gortok 2y agoThere is a correlation between new code and memory vulnerabilities (a possible explanation is given in the blog post, that vulnerabilities have a half-life that decays rapidly), but why does the blog post indicate causation between the two factors? There is more than one possible and reasonable explanation for this correlation: 1. New code often relates to new features, and folks focus on new features for vulnerabilities. 2. Older code has been through more real life usage, which can exercise those edge cases where memory vulnerabilities reside. I’m just not comfortable saying new code causes memory vulnerabilities and that vulnerabilities have a half-life that decays rapidly. That may —- may be true in sheer number count, but doesn’t seem to be true in impact, thinking back to the high-impact vulnerabilities in OSS like the heartbleed bug, and the cache-invalidation bugs for CPUs.
- anymouse123456 2y agoI'm here with you for the downvotes. This essay takes some interesting data from very specific (and unusual) projects and languages from a very specific (and unusual) culture and stridently extrapolates hard numeric values to all code without qualification. > For example, based on the average vulnerability lifetimes, 5-year-old code has a 3.4x (using lifetimes from the study) to 7.4x (using lifetimes observed in Android and Chromium) lower vulnerability density than new code. Given this conclusion, I can write a defect-filled chunk of C code and just let it marinate for 5 years offline in order for it become safe? I'm pretty sure there are important data in this research and there is truth underneath what is being shared, but the unsupported confidence and overreach of the writing is too distracting for me.
- throwaway2037 2y agoWhy is this downvoted? It raises some interesting and important issues. I saw a big bank's back office settlement system once. 30+ years old with almost no unit tests. It changes very little now and is virtually bug free because people have been fixing bugs in it for 30+ years! When they need to make changes these days, they first write unit tests for existing behaviour, then fix the bug. It is an example of how code can mature with a very low defect rate with limited unit tests.
- kernal 2y ago>Note that the data for 2024 is extrapolated to the full year (represented as 36, but currently at 27 after the September security bulletin). The reduction of memory safety bugs to a projected 36 in 2024 for Android is extremely impressive.
- musicale 2y ago"The net result is that a PL/I programmer would have to work very hard to program a buffer overflow error, while a C programmer has to work very hard to avoid programming a buffer overflow error." https://www.acsac.org/2002/papers/classic-multics.pdf https://www.acsac.org/2002/papers/classic-multics.pdf
- pjmlp 2y agoUNIX being free beer is the root cause of what we are still trying to fix.
- musicale 2y agoAre you implying that proprietary UNIX wasn't written in C? Or that (like the internet perhaps) it was basically a research prototype with minimal support for security or reliability that was accidentally (and perhaps inadvisedly) turned into something that people use and depend on?