Memory Safety's Hardest Problem

matklad Papers

Summary

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">&quot;std&quot;</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">&amp;</span>main);</span> <span class="line"></span> <span class="line"> <span class="hl-keyword">var</span> e: E = .{ .b = <span class="hl-string">&quot;hello&quot;</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 =&gt; <span class="hl-keyword">unreachable</span>,</span> <span class="line"> .b =&gt; <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">&lt;&lt;</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">&quot;{s}<span class="hl-string">\n</span>&quot;</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>
Original Article
View Cached Full Text

Cached at: 07/20/26, 09:23 PM

# 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)

Similar Articles

Safe Made Easy Pt.1: Single Ownership is (Not) Optional

Lobsters Hottest

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.

Memory safety is a matter of life and death

Lobsters Hottest

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.

The Edge of Safe Rust

Lobsters Hottest

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.

Discussion about C array type semantics

Lobsters Hottest

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.