9 ms·
How to Secure Existing C and C++ Software Without Memory Safety [pdf]
- m00dy 2y agoLooks like they're finally coming to terms with C++'s flaws.
- michaelsshaw 2y ago"they"
- pjc50 2y agoShort paper, so can be easily summarized. The claim is that security can be improved by these compiler and hardware assisted measures: - Stack Integrity - Control-Flow Integrity - Heap Data Integrity - Pointer Integrity and Unforgeability They cite the deployment of these measures on recent Apple hardware as evidence of their effectiveness.
- pastage 2y agoExciting! I did not know about HW pointer authentication and it is ten years old now on ARM, the referenced papers are nice but honestly LLVM docs are a lot better. https://clang.llvm.org/docs/PointerAuthentication.html https://clang.llvm.org/docs/PointerAuthentication.html The papers: https://www.usenix.org/conference/usenixsecurity23/presentation/cai-zechao https://www.usenix.org/conference/usenixsecurity23/presentat... https://www.qualcomm.com/content/dam/qcomm-martech/dm-assets/documents/poinyter-auth-v7.pdf https://www.qualcomm.com/content/dam/qcomm-martech/dm-assets...
- MattPalmer1086 2y agoThis looks really useful. Doesn't fix the problem of memory corruption but mostly seems to limit the ability to convert that into remote code execution. And all the techniques are already in widespread use, just not the default or used together. I would not be surprised if attackers still manage to find sneaky ways to bypass all the 4 protections, but it would certainly raise the bar significantly.
- lambdaone 2y agoIt's already been demonstrated to be insufficient; otherwise Apple software would now be impregnable. That's not to say these protections are a bad idea; they should be universal as they substantially reduce the existing attack surface - but the paper massively over-sells them as a panacea.
- pron 2y agoMost server software these days is already written in memory-safe languages yet still has security vulnerabilities. If you reduce the vulnerabilities due to memory safety issues by, say, 90%, then there's nothing special about them anymore. On the other hand, memory-safe languages also don't fully eliminate memory safety issues as they depend on operations or components that may not themselves be fully memory-safe (BTW, Rust has this problem more than other memory-safe languages). So a "panacea" in this context doesn't mean making the stack memory-safe (which nobody does, anyway) but rather making the vulnerabilities due to memory safety no more common or dangerous than other causes of vulnerabilities (and remember that rewriting software in a new language also introduces new vulnerability risks even when it reduces others). Whether these techniques actually accomplish that or not is another matter (the paper makes empirical claims without much evidence, just as some Rust fans do).
- kobebrookskC3 2y agowhat vulnerabilities does rust introduce over c/c++?
- TheDong 2y ago> BTW, Rust has this problem more than other memory-safe languages Do you have a citation there? I've run into a ton of memory safety issues in Go because two goroutines concurrently modifying a map or pointer is a data-race, which leads to memory unsafety... and go makes it wildly easy to write such data-races. You need to manually add mutexes everywhere for go to be memory safe. Rust, on the other hand, I've yet to run into an actual memory safety issue. Like, I'm at several hundred in Go, and 0 in rust. I'm curious why my experience is so different.
- sebstefan 2y ago>A Pragmatic Security Goal >Remote Code Execution (RCE) attacks where attackers exploit memory-corruption bugs to achieve complete control are a very important class of potentially-devastating attacks. Such attacks can be hugely disruptive, even simply in the effects and economic cost of their remediation [26]. Furthermore, the risk of such attacks is of special, critical concern for server-side platform foundations [10]. Greatly reducing the risk of RCE attacks in C and C++ software, despite the presence of memory-corruption bugs, would be a valuable milestone in software security especially if such attacks could be almost completely prevented. We can, therefore, aim for the ambitious, pragmatic goal of preventing most, or nearly all, possibilities of RCE attacks in existing C and C++ software without memory safety. Given the urgency of the situation, we should only consider existing, practical security mechanisms that can be rapidly deployed at scale. I don't know if it's obvious to anyone else that this is AI-written or if it's just me/if I'm mistaken
- dgellow 2y agoIt’s not obvious to me. I cannot say one way or the other
- readingnews 2y agoI am not sure, and it may be this persons culture/background, but I do know that at a college/uni, your advisors/reviewers would tell you not to do the adjective/drama stuff, as it adds no real value to a scientific/technical paper. e.g. potentially-devastating, hugely disruptive, special critical, greatly reducing, valuable milestone, almost completely, ambitious pragmatic, most or nearly all, existing practical.
- cadamsdotcom 2y ago> For high assurance, these foundations must be rewritten in memory- safe languages like Go and Rust [10]; however, history and estimates suggest this will take a decade or more [31]. The world runs on legacy code. CISA is correct that rewrites are needed for critical software [1][2] but we know how rewrites tend to go, and ROI on a rewrite is zero for most software, so it will take far more than a decade if it happens at all. So score one for pragmatism with this paper! Hope CISA folks see it and update their guidance. [1] https://www.cisa.gov/news-events/news/urgent-need-memory-safety-software-products https://www.cisa.gov/news-events/news/urgent-need-memory-saf... [2] https://www.cisa.gov/resources-tools/resources/case-memory-safe-roadmaps https://www.cisa.gov/resources-tools/resources/case-memory-s...
- berratype 2y agoI don't know if the ROI on rewrites is zero, following Prossimo's work, I'm seeing lots of performance and maintainability improvements over existing software.
- DyslexicAtheist 2y ago> Hope CISA folks see it and update their guidance. cope > hope @ CISA right now: https://www.darkreading.com/cyberattacks-data-breaches/cisa-cuts-dangerous-gamble-dangerous-world https://www.darkreading.com/cyberattacks-data-breaches/cisa-...
- 0xEF 2y agoI wish HN readers were not so afraid to openly discuss this more, but it is a much bigger issue than even what that article lets on (which is a good article, I might add). Cuts to critical agencies like this aren't about efficiency at all; it's about ham-stringing roadblocks that might be in your way. That means we can expect one of two things in the near future of the US; 1. Enemies of the US gov't exploit the weakness, possibly conspiring with the people who created the weakness to do so. 2. A near or complete collapse of the government as, lo and behold, it is discovered that none of them actually knew what they were doing, regardless of the confidence in which they said otherwise. Either way, we, the people trying to keep industries moving and bring home food to put on the table, will suffer.
- lambdaone 2y agoFrom the cited paper: "These four types of integrity, do not establish memory safety, but merely attempt to contain the effects of its absence; therefore, attackers will still be able to change software behavior by corrupting memory." and the paper then goes on to say, about Apple's implementation of the cited techniques: "This intuition is borne out by experience: in part as a result of Apple’s deployment of these defenses since 2019, the incidence of RCE attacks on Apple client software has decreased significantly—despite strong attack pressure—and the market value of such attacks risen sharply." "Decreased significantly" is not "eliminated"; indeed, you could paraphrase this as "the combination of these techniques has already been shown to be insufficient for security guarantees". Which is not to say that these mitigations are a bad idea; but I think their benefits are significantly over-sold in the paper.
- commandersaki 2y agoI said this in another comment, but an easy way to measure the efficacy is to look at the economy surrounding the zero day markets. Long story short, over the years it has become increasingly more expensive to both produce/supply and acquire exploits. This is attributable to the increasing mitigations and countermeasures, which include the ones in this document.
- MattPalmer1086 2y agoI don't think it is significantly oversold, although it is definitely overselling a bit: "More disconcertingly, in some cases, attacks may be still be possible despite the above protections." "We should prepare for a case where these defenses will fail to protect against a specific vulnerability in some specific software". My main concern with the paper is there is no careful analysis showing that the 4 techniques they propose are really sufficient to cover the majority of RCE exploits. Having said that, I don't dispute that having them would raise the bar for them a lot.
- mre 2y ago> However, their use is the exception, not the rule, and their use—in particular in combination—requires security expertise and investment that is not common. For them to provide real-world, large-scale improvements in the security outcomes of using C and C++ software, there remains significant work to be done. In particular, to provide security benefits at scale, for most software, these protections must be made an integral, easy-to-use part of the world-wide software development lifecycle. This is a big change and will require a team effort. That's the core problem. The mechanisms mentioned are primarily attack detection and mitigation techniques rather than prevention mechanisms. Bugs can't be exploited as easily, but they still exist in the codebase. We're essentially continuing to ship faulty software while hoping that tooling will protect us from the worst consequences. Couldn't one argue that containers and virtual machines also protect us from exploiting some of these memory safety bugs? They provide isolation boundaries that limit the impact of exploits, yet we still consider them insufficient alone. It's definitely a step in the right direction, though. The paper mentions Rust, so I wanted to highlight a few reasons why we still need it for people who might mistakenly think this approach makes Rust unnecessary: - Rust's ownership system prevents memory safety issues at compile time rather than trying to mitigate their effects at runtime - Rust completely eliminates null pointer dereferencing - Rust prevents data races in concurrent code, which the paper's approach doesn't address at all - Automatic bounds checking for all array and collection accesses prevent buffer overflows by design - Lifetimes ensure pointers are never dangling, unlike the paper's approach which merely tries to make dangling pointers harder to exploit So, we still need Rust, and we should continue migrating more code to it (and similar languages that might emerge in the future). The big idea is to shift bug detection to the left: from production to development.
- tgv 2y agoOr Java, or Scala, or Go, or whathaveyou. This is about existing software.
- unscaled 2y agoWhile all of the languages you mention are memory safe (as is almost every programming language released after 1990), none of them solve all of the safety problems mentioned above, in particular, the two points: - Rust completely eliminates null pointer dereferencing - Rust prevents data races in concurrent code, which the paper's approach doesn't address at all Scala comes closest to solving these points, since it has optional features (in Scala 3) to enable null safety or you could build some castles in the sky (with Scala 2) to avoid using null and make NPEs more unlikely. The same goes for concurrency bugs: you can use alternative concurrency models that make data races harder (e.g. encapsulate all your state inside Akka actors). With Go and Java there is no dice. These languages lack the expressive power (by design! since they are touted as "simple" languages) to do anything that will greatly reduce these types of bugs, without resorting external tools (e.g. Java static analyzers + annotation or race condition checkers). In short, Rust is one of the only mainstream languages that absolutely guarantees safety from race conditions, and complete null safety (most other languages that provide good null safety mechanisms like C#, Kotlin and TypeScript are unsound due to reliance on underlying legacy platforms).
- awaymazdacx5 2y agoC and C++ code is paradigmatic in being susceptible to CLI security vulnerabilities. Object-oriented languages typically work in the set A-Z, with limited characters, parameters, etc... Whereas Wittgenstein's concept of the private language is internal and discursive in Skinnerite probabalistic capacities.
- saagarjha 2y agoThere is actually an interesting niche that one can carve out when dealing with an attacker who has a memory corruption primitive but this paper is a bit too simple to explore that space. Preventing RCE is too broad of a goal; attackers on the platforms listed continue to bypass implementations of the mitigations presented and achieve some form of RCE. The paper suggests these are because of implementation issues, and some are clearly bugs in the implementation, but many are actually completely novel and unaddressed workarounds that require a redesign of the mitigation itself. For example, “heap isolation” can be done by moving allocations away from each other such that a linear overflow will run into a guard page and trap. Is it an implementation bug or a fundamental problem that an attacker can then poke bytes directly into a target allocation rather than linearly overwriting things? Control flow integrity has been implemented but attackers then find that, in a large application, calling whole functions in a sequence can lead to the results they want. Is this a problem with CFI or that specific implementation of CFI? One of the reasons that memory safety is useful is that it’s a lot easier to agree on what it is and how to achieve it, and with that what security properties it should have. Defining the security properties of mitigations is quite a bit harder. That isn’t to say that they’re not useful, or can’t be analyzed, but generally the result is not actually denial of RCE.
- cartalk 2y agoThis paper would have been really compelling in 2005-2010, but in 2025 there's too much evidence that these approaches do not result in C++ that is secure. The author cites a number of projects that have broadly applied these techniques, like Chrome and iOS, but these code bases continue to be exploited regularly despite these protections. If you actually look at where those projects are investing, it's on moving to Rust/Swift.
- xianga 2y agoNot to be harsh, but this article is messy, and I find myself agreeing with the commencts regarding hyperboles and AI-writing. It's a directionless nothingburger that hasn't really made any efforts to look into recent research on memory allocation and hardening. Some examples: In the heap-section, the article only cites ±20 year old papers and mixes user- and kernel-space allocators such as Apple's kernel kalloc_type and one for Chrome. Not to mention that the author talks about implemention heap regions per-object as if that wont introduce significant memory and performance overhead and is completely unrealistic in a comodity OS, let alone in a server setting. The pointer integrity section ends with the statement "The above protections, in combination, can prevent complete software control by attackers able to corrupt memory." - which is not true (see recent CVE i found after a quick search [1]), even for relativly hardened Apple products that use pointer authentication and a type segregated allocator kalloc_type. Additionally, literally the next sentence in the subsequent section contradicts the previous statement (!): "...therefore, attackers will still be able to change software behavior by corrupting memory". Really dampens the credibility. If anyone is interested in some recent contributions in the space, I've been looking into these recently (in no particular order): SeaK [2], ViK[3] and Safeslab [4]. [1] https://www.cvedetails.com/cve/CVE-2025-24085/ https://www.cvedetails.com/cve/CVE-2025-24085/ [2] https://www.usenix.org/conference/usenixsecurity24/presentation/wang-zicheng https://www.usenix.org/conference/usenixsecurity24/presentat... [3] https://dl.acm.org/doi/10.1145/3503222.3507780 https://dl.acm.org/doi/10.1145/3503222.3507780 [4] https://dl.acm.org/doi/10.1145/3658644.3670279 https://dl.acm.org/doi/10.1145/3658644.3670279
- gizmo 2y agoThe memory protection strategies this paper argues for are fine. If we can recompile legacy software to gain better protection against stack and heap exploits that's a clear win. As the paper points out memory safety is not a great concern on phones because applications are sandboxed. And that's correct. If an application is stuck in a sandbox it doesn't matter what that process does within its own process space. Smartphones taught us what we already knew: process isolation works. Then the paper observes that memory safety is still a significant problem on the server. But instead of pointing out the root cause -- the absence of sandboxing -- the authors argue that applications should instead be rewritten in go or rust! This is absurd. The kernel already provides strong memory protection guarantees for each process. The kernel also provides hard guarantees for access to devices and the file system. But server software doesn't take advantage of any of these guarantees. When a server process intermixes data of multiple customers and privilege levels then any tiny programming mistake (regardless of memory safety) can result in privilege escalation or catastrophic data leaks. What use is memory safety when your go program returns the wrong user's data because of an off-by-one error? You don't need a root exploit if your process already has "root access" to the database server. If we want to be serious about writing secure software on the server we have to start taking advantage of the process isolation the kernel provides. The kernel can enforce that a web request from user A cannot return data from user B because the process simply cannot open any files that belong to the wrong user. This completely eliminates all memory safety concerns. But today software on the server emulates what the kernel already does with threading, scheduling, and memory protection, except poorly and in userspace and without any hardware guarantees. Effectively all code runs as root in ring 0. And we're surprised that security continues to plague our industry?
- tonyhart7 2y agocommenting before reading it but I guess memory arena???
- pizlonator 2y agoI think this paper overestimates the benefit of what I call isoheaps (partitioning allocations by type). I wrote the WebKit isoheap implementation so it’s something I care about a lot. Isoheaps can make mostly neutralize use after free bugs. But that’s all they do. Moreover they don’t scale well. If you isoheap a select set of stuff then it’s fine, but if you tried to deploy isoheaps to every allocation you get massive memory overhead (2x or more) and substantial time overhead too. I know because I tried that. If an attacker finds a type confusion or heap buffer overflow then isoheaps won’t prevent the attacker from controlling heap layout. All it takes is that they can confuse an int with a ptr and game over. If they can read ptr values as ints then they can figure out how the heap is laid out (no matter how weirdly you laid it out). If they also can write ptr values as ints then control they whole heap. At that point it doesn’t even matter if you have control flow integrity. To defeat attackers you really need some kind of 100% solution where you can prove that the attacker can’t use a bug with one pointer access to control the whole heap.
- Yoric 2y agoYes, having coded quite a few years in C++ (on the Firefox codebase) before migrating to Rust, I believe that many C and C++ developers mistakenly assume that 1/ memory safety is the unachievable holy grail of safety ; 2/ there is a magic bullet somewhere that can bring about the benefits of memory safety, without any of the costs (real or expected). In practice, the first assumption is wrong because memory-safety is just where safety _starts_. Once you have memory-safety and type-safety, you can start building stuff. If you have already expended all your cognitive budget on reaching this point, you have lost. As for the magic bullets, all those I've seen suggested are of the better-than-nothing variety rather than the it-just-works variety they're often touted as. Doesn't mean that there won't ever be a solution, but I'm not holding my breath. And of course, I've seen people claim more than once that AI will solve code safety & security. So far, that's not quite what's written on the wall.
- gpderetta 2y agoWell, GC is very close to that magic bullet (comparatively spatial safety via bound checking is easy). It does have some costs of course, especially in a language like C++ that is GC-hostile.
- tzs 2y agoOT: Are there any memory safe languages that that are fast and support goto? I'm writing something that needs to implement some tax computations and I want to implement them to follow as closely as possible the forms that are used to report those computations to the government. That way it is easy to be sure they are correct and easy to update them if the rules change. The way those forms work is something like this: 1. Enter your Foo: _________ 2. Enter your Bar: _________ 3. Add line 1 and line 2: ________ 4: Enter your Spam: _______ 5: Enter the smaller of line 1 and 4: _____ 6: If line 5 is less than $1000 skip to line 9 7: Enter the smaller of line 2 and $5000: _____ 8: If line 7 is greater than line 4 skip to 13 ... With goto you can write code that exactly follows the form: Line1: L1 = Foo; Line2: L2 = Bar; Line3: L3 = L1 + L2; Line4: L4 = Spam; Line5: L5 = min(L1, L4); Line6: if (L5 < 1000) goto Line9; Line7: L6 = min(L2, 5000); Line8: if (L7 > L4) goto Line13; ... For some forms an if (X) goto Y .... Y: can be replaced by if (!X) { ... } because nothing before that has a goto into the body of the if statement. But some forms do have things jumping into places like that. Also jumping out of what would be such a body into the body of something later. Writing those without goto tends to require duplicating code. The duplication in the source code could be eliminated with a macro system but don't most memory safe languages also frown on macro systems? Putting the duplicate code in separate functions could also work but often those sections of code refer to things earlier in the form so some of the functions might need a lot of arguments. However the code then doesn't look much like the paper form so it is harder to see that it is correct or to update it when the form changes in different years.
- ameliaquining 2y agoRust has macros. It also has labeled blocks that you can break out of, which are similar to goto except with more nesting required. You could plausibly reduce the nesting with a macro though. In most languages I'd just solve this by ending each block with a call to the next block. The "too many arguments required" problem can be addressed with closures.
- returningfory2 2y agoMaybe you could build a domain-specific language.
- linux_security 2y agoWas discussing this paper with a few colleagues who work in this area, and concluded that this paper seems like an odd combination of: - The author citing their own research. (Ok, all researchers do this) - Mildly scolding the industry for not having applied their research. It's "pragmatic" after all. The elephant in the room is that these approaches have been widely deployed and their track record is pretty questionable. iPhone widely deploys PAC and kalloc_type. Chrome applies CFI and PartitionAlloc. Android applies CFI and Scudo. Yet memory safety exploitation still regularly occurs against these targets. Is it harder because of these technologies? Probably. But if they're so effective, why are attackers still regularly successful at exploiting memory safety bugs? And what's the cost of applying these? Does my phone's battery die sooner? Is it slower? So now your phone/browser are slower AND still exploitable.
- comex 2y agoThe paper significantly overstates the scope of PAC (pointer authentication codes) on Apple platforms. To quote the paper: > This is effectively what is done in Apple software, which uses special ARM hardware support to also check pointer integrity at runtime—i.e., ensure each pointer access uses pointers of the right type[]. Apple uses this further to enforce a form of stack integrity, control-flow integrity, and heap integrity In reality, the compiler only automatically applies PAC to code pointers: stack return addresses, function pointers, and C++ vtable method pointers. You can also manually apply PAC to other pointers using attributes and intrinsics; this is used by components like the Objective-C runtime and the memory allocator. But only a tiny amount of code does this. PAC is nevertheless a powerful mitigation. But let’s see how it stacks up against the paper’s claims: - Heap integrity: Since Apple’s implementation of PAC leaves most data pointers unsigned, it has little direct relevance to heap integrity. The paper seems to want to sign all pointers. You could theoretically implement a compiler feature to use PAC instructions for all pointers, but I don’t think anyone (not just Apple) has implemented such a thing. It would probably come with high performance and compatibility costs. - Stack integrity: The paper defines this as “attackers cannot change the arguments, local variables, or return sites”. PAC makes it difficult to change return sites, but does nothing to prevent changing arguments and local variables (unless you're limited to a linear overwrite). Again, it's theoretically possible to use PAC instructions to secure those things: there is a technique to make a single signature that combines multiple pointer-sized values, so you could try to make one signature that covers the whole set of local variables and other stack bits. But nobody does this, so the compatibility and performance costs are unknown. Even SafeStack (which the paper also cites) does not fully protect local variables, though it gets closer. - Control-flow integrity: The paper mentions “type signatures”, but Apple’s PAC-based CFI does not validate type signatures for C function pointers, only vtables and Objective-C isa pointers. Other CFI implementations do validate C function pointer type signatures, like Android, though this seems to come at the cost of a slower pace of adoption. More importantly, attackers have demonstrated the ability to get around CFI, by substituting valid but irrelevant function pointers to achieve “jump-oriented programming” (JOP). Project Zero recently published a blog post explaining a (semi-recent) iOS exploit that used this technique: https://googleprojectzero.blogspot.com/2025/03/blasting-past-webp.html https://googleprojectzero.blogspot.com/2025/03/blasting-past... I’m not sure whether type signature validation would have prevented this particular exploit, but many C function pointers have pretty simple signatures (yet may do wildly different things), so the benefit is somewhat limited.
- nickpsecurity 2y agoI've always favored a large public/private investment into open-source tools like Coverity, PVS Check, and RV-Match. Put extra effort into suppressing false positives and autofixing simple problems. Companies like Apple had enough money to straight up buy the vendors of these tools. I'd also say, like CPAChecker and Why3, they should be designed in a flexible way where different languages can easily be added. Also, new passes for analyzers. Then, just keep running it on all the C/C++ code in low, false, positive mode. On top of this, there have been techniques to efficiently do total memory safety. Softbound + CETS was an example. We should invest in more of those techniques. Then, combine the analyzers with those tools to only do runtime checks on what couldn't be proven.