This article discusses a fundamental memory safety challenge involving tagged unions where a pointer to one variant is used after the union is overwritten with a different variant, leading to type confusion. The author also argues that buffer overflows are the most exploitable memory error and could have been mitigated with better array syntax.
<header>
<h1>Memory Safety’s Hardest Problem</h1>
<time class="meta" datetime="2026-07-20">Jul 20, 2026</time>
</header>
<p>Uplifting a
<a href="https://lobste.rs/s/vzkmtj/forget_borrow_checkers_c3_solved_memory#c_uuhbpy">lobsters comment</a>
for easier reference.</p>
<p>The central memory safety counter example, the hardest case to solve, doesn’t have anything to do
with destructors or heap:</p>
<figure class="code-block">
<pre><code><span class="line"><span class="hl-keyword">const</span> std = <span class="hl-built_in">@import</span>(<span class="hl-string">"std"</span>);</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">const</span> E = <span class="hl-keyword">union</span>(<span class="hl-keyword">enum</span>) {</span>
<span class="line"> a: <span class="hl-type">u128</span>,</span>
<span class="line"> b: []<span class="hl-keyword">const</span> <span class="hl-type">u8</span>,</span>
<span class="line">};</span>
<span class="line"></span>
<span class="line"><span class="hl-keyword">pub</span> <span class="hl-keyword">fn</span><span class="hl-function"> main</span>() <span class="hl-type">void</span> {</span>
<span class="line"> <span class="hl-keyword">const</span> bad_addr: <span class="hl-type">u128</span> = <span class="hl-built_in">@intFromPtr</span>(<span class="hl-operator">&</span>main);</span>
<span class="line"></span>
<span class="line"> <span class="hl-keyword">var</span> e: E = .{ .b = <span class="hl-string">"hello"</span> };</span>
<span class="line"> <span class="hl-keyword">const</span> oh_no_pointer: <span class="hl-operator">*</span><span class="hl-keyword">const</span> []<span class="hl-keyword">const</span> <span class="hl-type">u8</span> = <span class="hl-keyword">switch</span> (e) {</span>
<span class="line"> .a => <span class="hl-keyword">unreachable</span>,</span>
<span class="line"> .b => <span class="hl-operator">|</span><span class="hl-operator">*</span>p<span class="hl-operator">|</span> p,</span>
<span class="line"> };</span>
<span class="line"> e = .{ .a = (<span class="hl-numbers">16</span> <span class="hl-operator"><<</span> <span class="hl-numbers">64</span>) <span class="hl-operator">+</span> bad_addr };</span>
<span class="line"> <span class="hl-keyword">const</span> oh_no: []<span class="hl-keyword">const</span> <span class="hl-type">u8</span> = oh_no_pointer.<span class="hl-operator">*</span>;</span>
<span class="line"> std.debug.print(<span class="hl-string">"{s}<span class="hl-string">\n</span>"</span>, .{oh_no});</span>
<span class="line">}</span></code></pre>
</figure>
<figure class="code-block">
<pre><code><span class="line">$ zig run main.zig</span>
<span class="line">��C�� �</span></code></pre>
</figure>
<p>This sort of example also breaks Ada:</p>
<p><a href="https://www.enyo.de/fw/notes/ada-type-safety.html" class="url">https://www.enyo.de/fw/notes/ada-type-safety.html</a></p>
<p>We have a tagged union, which can hold either <code>A</code> or <code>B</code>. We initialize the union as
<code>A</code>, take a pointer to its internals, overwrite the original with <code>B</code>, and then use the pointer. The
pointer is still typed as <code>A</code>, but the bytes it points to now belong to <code>B</code>: a type confusion.</p>
<hr>
<p>This being said, we care about memory unsafety primarily because it leads to exploitable software,
and it’s unclear just how impactful the example above is in practice. It is a happy coincidence that
by far the most exploitable memory error in practice, the infamous buffer overflow, is also trivial
to fix with compiler-inserted bounds checks. The biggest miss of the industry when it comes to
memory safety is not listening to Walter Bright:</p>
<p><a href="https://digitalmars.com/articles/C-biggest-mistake.html" class="url">https://digitalmars.com/articles/C-biggest-mistake.html</a></p>
<p>I bet that, had we got <code>char a[..]</code> syntax around C11, quite a few issues wouldn’t have happened!</p>
<p>See also <a href="https://matklad.github.io/2025/12/30/memory-safety-is.html"><em>What is Memory Safety?</em></a></p>
# Memory Safety's Hardest Problem
Source: [https://matklad.github.io/2026/07/20/memory-safety-hardest-problem.html](https://matklad.github.io/2026/07/20/memory-safety-hardest-problem.html)
Jul 20, 2026Uplifting a[lobsters comment](https://lobste.rs/s/vzkmtj/forget_borrow_checkers_c3_solved_memory#c_uuhbpy)for easier reference\.
The central memory safety counter example, the hardest case to solve, doesn’t have anything to do with destructors or heap:
```
const std = @import("std");
const E = union(enum) {
a: u128,
b: []const u8,
};
pub fn main() void {
const bad_addr: u128 = @intFromPtr(&main);
var e: E = .{ .b = "hello" };
const oh_no_pointer: *const []const u8 = switch (e) {
.a => unreachable,
.b => |*p| p,
};
e = .{ .a = (16 << 64) + bad_addr };
const oh_no: []const u8 = oh_no_pointer.*;
std.debug.print("{s}\n", .{oh_no});
}
```
```
$ zig run main.zig
��C�� �
```
This sort of example also breaks Ada:
[https://www\.enyo\.de/fw/notes/ada\-type\-safety\.html](https://www.enyo.de/fw/notes/ada-type-safety.html)
We have a tagged union, which can hold either`A`or`B`\. We initialize the union as`A`, take a pointer to its internals, overwrite the original with`B`, and then use the pointer\. The pointer is still typed as`A`, but the bytes it points to now belong to`B`: a type confusion\.
---
This being said, we care about memory unsafety primarily because it leads to exploitable software, and it’s unclear just how impactful the example above is in practice\. It is a happy coincidence that by far the most exploitable memory error in practice, the infamous buffer overflow, is also trivial to fix with compiler\-inserted bounds checks\. The biggest miss of the industry when it comes to memory safety is not listening to Walter Bright:
[https://digitalmars\.com/articles/C\-biggest\-mistake\.html](https://digitalmars.com/articles/C-biggest-mistake.html)
I bet that, had we got`char a\[\.\.\]`syntax around C11, quite a few issues wouldn’t have happened\!
See also[*What is Memory Safety?*](https://matklad.github.io/2025/12/30/memory-safety-is.html)
This article introduces a new approach to memory safety based on linear types and abstract interpretation, aiming to eliminate common bugs like use-after-free and memory leaks more ergonomically than Rust.
The author argues that memory-unsafe open-source software is critically vulnerable to upcoming AI bug-finding agents, making memory safety a moral imperative, and that Rust must succeed as the leading memory-safe language with no overhead.
An analysis of how memory safety CVEs are reported differently in Rust vs C/C++, arguing that Rust's design reduces certain classes of vulnerabilities even when bugs exist.
A TokioConf 2026 talk/blog post explores pushing safe Rust to its limits by implementing tracing garbage collection for complex pointer structures, sharing techniques for circular references and raw-pointer GC design.
The article explains the confusing behavior of C array types, including their decay to pointers, exceptions like sizeof and function parameters, and compares it to function types, suggesting a mental model where arrays and pointers are strictly separated.