Memory Safety's Hardest Problem

surprisetalk1 pts0 comments

Memory Safety's Hardest Problem

Uplifting a<br>lobsters comment<br>for easier reference.

The central memory safety counter example, the hardest case to solve, doesn’t have anything to do<br>with destructors or heap:

const std = @import("std");

const E = union(enum) {<br>a: u128,<br>b: []const u8,<br>};

pub fn main() void {<br>const bad_addr: u128 = @intFromPtr(&main);

var e: E = .{ .b = "hello" };<br>const oh_no_pointer: *const []const u8 = switch (e) {<br>.a => unreachable,<br>.b => |*p| p,<br>};<br>e = .{ .a = (16 64) + bad_addr };<br>const oh_no: []const u8 = oh_no_pointer.*;<br>std.debug.print("{s}\n", .{oh_no});

$ zig run main.zig<br>��C���

This sort of example also breaks Ada:

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<br>A, take a pointer to its internals, overwrite the original with B, and then use the pointer. The<br>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,<br>and it’s unclear just how impactful the example above is in practice. It is a happy coincidence that<br>by far the most exploitable memory error in practice, the infamous buffer overflow, is also trivial<br>to fix with compiler-inserted bounds checks. The biggest miss of the industry when it comes to<br>memory safety is not listening to Walter Bright:

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?

const memory safety hardest example union

Related Articles