In this post, I want to respond to the RAND report (https://www.rand.org/content/dam/rand/pubs/perspectives/PEA4900/PEA4957-1/RAND_PEA4957-1.pdf).

Their basic premise is correct: we are distracted using AI hunting for vulnerabilities and should instead use AI to harden code, to perform a large refactoring step.

But that’s not really the point of the report. Instead, it’s “Rust advocacy”. Rust is a “systems” language designed to replace C/C++. They aren’t trying to solve this problem so much as exploit this problem to advocate for Rust.

Let’s talk about C for a moment

The C programming language is famous for two reasons.

The first is that it’s the world’s “systems” language. Most all of the world’s core code is written in it, including operating systems, libraries, and core utilities. It’s the foundation infrastructure language of modern computing.

The second famous thing is that it’s not “memory safe”. This allows a class of bugs like “buffer overflows” that allows hackers to break into code.

The two problems are related. A systems language needs raw control over the computer hardware, and it’s this raw control that causes problems.

Rust to the rescue

Rust is the most popular alternative to C, a “memory safe systems language”. It has the same low-level features needed in a systems language, but at the same time, memory safety by default. There are other alternatives (like Zig), but none have the widespread support of Rust.

But Rust has problems.

The major problem is that it’s attempting to support a wide range of “powerful” features in the programming language loved by academics. This sounds good, but it results in brilliant programmers writing elegant code that average programmers cannot understand.

My pet peeve is the “mut” keyword. Rust makes variables immutable by default. There are lots of good reasons for this choice. Programmers shouldn’t mutate variables as much as they do. But it’s more a stylistic issue that can be addressed with style checkers rather than compilers.

The consequence is that when you actually need mutability of a variable, you need to add the “mut” keyword. This then dirties up the code, because as it turns out, you need mutability more than the designers thought.

The point is that Rust is a language that at its core is based upon advocacy. Trying to read Rust code means fighting with whatever the programmer who wrote it was advocating for.

An alternative for C

Memory safety can be solved in C directly, without needing rewrites in Rust. All that’s needed are backwards compatible extensions to the language. The solution would look something like the following.

struct packet {

size_t length;

unsigned char data[] COUNTED_BY(length);

};The trick is firstly what this does, marking the ‘length’ member as the bounds for the ‘data’ member. Secondly, the important feature is that it’s hidden behind a macro. That macro can be defined to be empty for older compilers, but defined as “__attribute__((counted_by(length)))” on Clang v18.

Such efforts have failed in the past because programmers have misunderstood backwards compatibility. Past efforts have always required forking the code, such that the new code no longer worked with older compilers, and no longer linked with older libraries.

When you fork the code, creating a new version, the old version remains surprisingly sticky. You end up having to maintain both versions moving forward, and often, the new version fails because people still want the old code.

Proper memory safe C extensions require no forking of the code.

C is slowly adding them to mainstream compilers. Apple has their own private extensions that work quite well, but they only work when writing code for Apple products. They should become more universal.

Alternatively, there are tools that the Linux kernel uses outside the compiler. They have separate “static analysis” tools that verify that certain functions do proper behavior. For example, instead of doing invisible bounds checking work underneath, they can verify that explicit bounds checking was done.

Advocacy for cybersecurity should just promote these extensions to C which are known to work well rather than dubious rewrites in Rust.

Another alternative for C

Even without language extensions, AI can already harden C. You can simply tell the AI to analyze a code base and fix unchecked bounds.

In other words, you would just write standard structures like the following and possibly add comments to communicate bounds information to the AI.

struct packet {

size_t length;

unsigned char data[]; /* bounded by 'length' */

};The AI would then analyze the code, check when they access pointers or arrays, and verify that some sort of bounds check was made before the access.

It’s not as certain or elegant as a language feature, but it’s something that would harden code right now.

I’ve played around with this sort of thing a bit. It works well in some libraries, like OpenSSL, which already tries to bounds check things. It works less well in ugly libraries like FFmpeg, which passes pointers around with no bounds information.

I mention “libraries” because they have “interfaces” between modules that cause problems. Sometimes those interfaces communicate bounds information (like in OpenSSL) and sometimes they don’t (like FFmpeg). Some the AI can easily check, verify, and point out weaknesses. Some the AI will demand a much larger refactoring effort.

Coordinated effort

Part of advocacy is that we need coordination and leadership to make this happen. This is a fallacy; such efforts are already being done.

The paper points to other coordinated efforts like Y2K, the transition to HTTPS, and vaccines eradicating smallpox.

There is a lot of disagreement about the Y2K issue. Yes, there was a lot of old code that needed to be fixed, but the claim was that “doom” would happen if there wasn’t a huge effort to fix every bug: planes would fall out of the air, stock markets would crash, nuclear missiles would launch themselves, and so forth.

I’m among those who reject the “Y2K doom” hypothesis.

Yes, we needed to solve some critical bugs. Yes, they should’ve spent a lot of effort eradicating any Y2K bug in anything related to airplanes.

But at the same time, finding and fixing bugs has “diminishing marginal returns”. The more effort you spend fixing Y2K bugs, the more it costs to find and fix the next one. Y2K doomerism drove people to spend $10 million to fix a bug that would’ve only cost $1 million had it remained unfixed.

As a society, we spent 10x more fixing Y2K bugs than was warranted.

We are faced with the same doomerism here. We can cheaply address the C memory safety issues, but the doomerists are instead demanding we follow the insane cost approach.

The document points to the transition to HTTPS. This is the opposite of the argument it wanted. This wasn’t some doomsday crisis that needed a coordinated effort to fix, but simply the natural evolution of technology.

The real message here is that memory safe C is coming whether or not there’s “leadership” or “coordination” driving the issue. You see that these projects are addressing a lot of vulnerabilities here, but these hardening steps are also happening alongside those fixes.

Finally, they talk about smallpox eradication and attempts at polio eradication.

The driving factors that made this work for smallpox (and not working so well for polio) have to do with details about the disease itself.

Smallpox leaves a very visible indication that a person was infected, with scars across their body and face. Therefore, it’s relatively easy for international workers to identify the existence of outbreaks to direct eradication efforts. Polio, in contrast, is invisible. Most people who contract it (and infect others) leave no physical identification that the disease happened.

The same is true here. We have a lot of classes of bugs that need to be addressed, and memory safety is only one type. Memory safety isn’t something that inherently needs any coordinated effort.

The point is that these cases don’t support the idea of coordinated leadership driving solutions.

How rather than what

Rather than focusing on “what” needs to be solved, restructuring code to be memory safe, the paper focuses on the “how”. It’s much more than simply advocating Rust, but also advocating a lot of other nonsense.

Refactoring code is a well understood problem, especially with all the “agile” coding philosophies. You don’t need to educate programmers on how to do this.

For example, it describes “specification lifting”, which sounds real smart, but it’s not a thing.

What they are talking about is the fact that code is supposed to have a well-defined “specification” describing what it’s supposed to do. The reality of code is that it doesn’t have such a thing, either because it never started with a clear specification, or that as the code evolved over time, no such document was updated. You may have such a specification by collecting all the Git commit comments back into a single document, but really, the raw code ends up being the specification.

Purists see this as some sort of moral weakness. Projects should have robust, detailed specifications, and it’s because of laziness and technical debt that they don’t have one.

In reality, it was always an unreasonable expectation to begin with. Critical code, like airplane software or crypto algorithms, needs detailed specifications, but the 99% of remaining code does not.

The futility of specifications is demonstrated by the famous Microsoft file formats and protocol specifications issue. Back around the year 2000 there were some court cases involving Microsoft’s monopoly status. With a consent decree, Microsoft opened up its file formats and network protocols to others. This allowed anybody to create a document that Microsoft Word could read, or a server that Microsoft’s network clients could access.

Almost immediately people sued Microsoft when their attempts to use the specifications failed. They claimed Microsoft was acting in bad faith by making their specifications incomplete. That’s not true. The reality is that the specification is the code itself, and it’s impossible to make a totally accurate specification without simply showing the code.

This problem bit Microsoft, too. They want their latest products to be able to read and convert files from older versions of their product. In other words, a document written back in the early 1990s with Microsoft Word 2.0 should still be readable by Microsoft Word 2026.

At the same time, the conversion software needed to be robust, such as by having memory safety features to prevent hackers posting documents that would take over people’s computers in the conversion software.

They found these goals, security and compatibility, couldn’t be solved at the same time. Old documents relied upon certain bugs, so you couldn’t fix them.

So what Microsoft did instead was create a special “sandbox” that let hackers mess around, but only within the sandbox.

The upshot is that “specification lifting” sounds good, but you’ll never get a proper specification in practice. What really matters is code.

This fact guides refactoring efforts. Any refactoring or rewrite will break things, important things, no matter what. That’s why people are conservative on refactoring efforts. That’s why a rewrite into Rust is problematic: it’ll cause even more to break. This isn’t a matter of spending a few months on the code, but often a matter of spending years trying to fix small things that broke.

During that time, customers will continue to use the old memory unsafe version of the project. So now you are maintaining two code bases, trying to add features to the old version while still fixing what broke in the new version.

My point is really that academics shouldn’t be advocating how to harden C code. Yes, promoting the problem is good, pointing out that whack-a-mole is bad compared to hardening effort. But telling people how to fix their code is very bad.

Policy recommendations

I’ve been in a lot of meetings with government policy makers pushing Biden’s “Secure By Design” initiatives, which were also thinly veiled Rust advocacy. It’s all such massive ignorance of the practicalities of what really is needed. Instead of addressing the problem, it requires a massive expenditure to deal with bureaucracy.

Things can be vastly simpler. If you want the DoD to buy products that are memory safe, simply ask the vendor to self-certify that they are memory safe. When a buffer-overflow bug is discovered in the software, well, then you have nice contract disputes where the vendor failed to deliver what was promised.

Such requirements quickly filter down. A lot of code is made with OpenSSL, and there are Rust rewrites of OpenSSL. But the core library is already mostly memory safe, where the remaining issues can easily be resolved with AI or simple C extensions. At some point, it’s going to self-certify as “memory safe”. Projects can likewise self-certify that they are memory safe, because all the components are self-certified memory safe.

And when they aren’t, because they won’t all be, we’ll be able to track down what went wrong and fix it.

Conclusion

My point is that this document makes a good statement, harden code instead of vuln whack-a-mole, but it’s really more about advocacy of their preferred solutions of Rust and government bureaucracy. It’s not to be taken seriously.

AI disclaimer: All the text is written by hand, but AI was used to proofread and fix typos (but not change my words). The image at the top is generated by AI.