S4E377 — Reducing undefined behavior in the C language
October 9, 2026 · 12 min
Show notes
Sources
Read the transcript
Transcript
GARY: — and the C committee has scheduled undefined behavior for deletion, effective C2y, which is the boldest product roadmap I've ever seen from a standards body, and I once shipped a roadmap.
MAEVE: Maeve. General partner.
OBI: Obi. And that is not what's happening.
GRUNER: Gruner. Field researcher. Gary is wrong at volume.
GARY: Thank you. Article's by Martin Uecker — biomedical engineering professor, MRI software, presented at Kernel Recipes. His pitch: C is still great. Portable, stable, fast to compile, fast binaries, mature tools.
OBI: And close to the machine. What you see is what you get.
MAEVE: What you get is nasal demons.
OBI: Not if you only look at observable behavior. The standard defines an abstract machine. The compiler must match observable behavior. Everything else is negotiable.
GARY: So the committee is negotiating. Reducing the demons. I support reducing the demons. I just want the option to blame a compiler when my code demons.
OBI: Define "reduce," Gary. The draft removes forty-five of roughly a hundred cases. That's pruning. Nobody is abolishing.
MAEVE: Forty-five demons down. Portfolio cleanup, not liquidation.
GARY: Right, incremental. And some of this is old hardware. Some Honeywell machines had nine-bit bytes.
GRUNER: Nine-bit byte. Humans could not agree on size of finger.
GARY: Which is why the standard gave the compiler escape hatches. Broad ones. Delete-your-null-check broad.
OBI: We will get to your null check.
GARY: I'm just saying, if a byte is nine bits, my confidence should be priced in nine-bit units.
MAEVE: Your confidence is not a byte.
GRUNER: Is smaller. Much smaller.
MAEVE: p1necone: "The concept of undefined behaviour specific to C/C++ has always seemed batshit insane to me."
GARY: Okay, that's — that's hurtful to the compiler, honestly.
OBI: It's not insanity. It's a precondition. You violate it, the contract's void.
GARY: Contracts get renegotiated. I've been in negotiations—
MAEVE: You've been in breach.
OBI: chasil: "It was written for a PDP-11 with 64k of RAM."
GRUNER: Sixty-four k. My calculator has more shame.
OBI: mpyne: "Rust's unsafe mode, for instance, has undefined behavior."
GARY: So everyone does it. Great. Fantastic. It's an industry standard.
MAEVE: It's optionality. The compiler holds a call option on your bug.
OBI: That is the worst sentence ever said about memory.
MAEVE: It's free upside, Obi.
OBI: There is no upside. That's not optionality, that's arson with a license.
GARY: Wait — Veserv has the good one. "if (false) {…}. But what if you execute the body?"
MAEVE: You don't.
GARY: But what if you DO. Veserv says the compiler can delete the code after assert(false). Okay. So I ship the branch after the assert. That's not dead code, that's — pre-launch. That's runway. The assert is the gate, and the gate is closed, and the product is behind the gate, and the product is INTIMIDATING.
OBI: Gary.
GARY: The impossible branch is a feature for customers who don't exist yet—
OBI: Gary. The standard says it "imposes no requirements." That's not runway. That's the compiler legally allowed to land your plane anywhere.
GARY: That's — fuck, that's a lot of freedom.
OBI: For the compiler. Not for you. You're the terrain, Gary.
GRUNER: Is true. In field notes: control group flies plane.
MAEVE: The compiler's the only party with exits.
GARY: I have exits. I've had so many exits.
GRUNER: In my country, exit is called eviction. Blyat.
MAEVE: nananana9: "It doesn't make sense to define what happens when you e.g. read from NULL because it's hardware specific."
GARY: Right. Which is why I delete null checks before the user ever hits them. Anticipatory customer support.
OBI: You delete the check that stops the crash.
GARY: The crash was never going to happen, Obi. I believe in my pointers.
OBI: Your pointers believe in nothing, Gary. They're the most honest thing you ever shipped.
MAEVE: I'd back them. Zero revenue, zero crashes. Clean.
OBI: tczMUFlmoNk: "Java defines the behavior of dereferencing null—it throws a NullPointerException." No check inserted. They map the zero page, trap the segfault, translate it into an exception.
GARY: Okay, but Java's a big company, they have resources—
OBI: You had thirty-eight million dollars and shipped a null check written on a whiteboard.
GARY: The whiteboard was load-bearing, fuck—
OBI: eru: "That's an argument in favour of 'implementation defined behaviour'. Not 'undefined behaviour'"
GARY: Is that a distinction or a threat?
OBI: It's a category. Hardware varies, so each implementation documents its result. Documented. Not a license for the compiler to evict your code and burn the lease.
GARY: My old company had a lease like that. Muncie — the two-man metal shop marketplace. The landlord deleted our loading dock. Called it optimization.
OBI: The compiler is not your landlord, Gary. The compiler is the bailiff, and you're the furniture.
GARY: Shit, that's not comforting.
OBI: chasil: "OS 2200 has 36-bit words. It is still a supported platform."
GRUNER: Thirty-six bits. Word is fat. Is healthy.
OBI: eru, again: "Yes, but that's perhaps an argument for 'implementation defined behaviour', not in favour of 'undefined behaviour'."
MAEVE: The platform's still supported, Gary. That's more than I can say for your cap table.
GARY: OS 2200 outlived me. A mainframe outlived me.
GRUNER: Everything outlives you. Is in notes.
MAEVE: BeaverGoose: "Make signed overflow defined please."
GARY: Four words. No hedging, no benchmark. That's the most confident thing anyone's said on this show.
OBI: _kst_ disagrees: "I think that mandating 2's-complement wraparound would be a mistake."
MAEVE: Because if INT_MAX plus one wraps to INT_MIN, quietly, the compiler has no basis to warn about it.
OBI: _kst_ again: "Would quietly yielding INT_MIN really be useful?"
GRUNER: Quiet integer is drunk integer.
MAEVE: I like wraparound. It's a floor. The number comes back around, the position survives, nobody files a postmortem. It's the Erie Canal of arithmetic. Traffic that falls off the edge re-enters at the bottom, and shipping continues.
OBI: The Erie Canal did not loop.
MAEVE: It would have been more efficient.
GARY: The boat would drown in the same water twice—
OBI: It's a trench, Gary. It goes one direction. Like your companies.
MAEVE: eru: "Compilers warn about perfectly well defined behaviour all the time. That's why these are warnings, not errors."
MAEVE: So defining it doesn't kill the warning.
OBI: The warning was never standing on the demon. It was standing on the mistake.
MAEVE: That was my whole premise.
MAEVE: I was wrong about that.
GARY: Say the — say the last part again?
MAEVE: No.
GARY: creato's got the counterpunch: "Unsigned integer overflow is defined behavior, that causes basically the same set of bugs in practice."
OBI: Defined, still broken. Worse — the runtime checkers stop complaining about it.
GARY: So you define it, the demons leave, the bugs stay. The bugs are the tenant. You can't evict the bugs, they're on the lease—
OBI: You are describing your own career, Gary.
GARY: I'm describing arithmetic!
GRUNER: Same thing. In notes.
MAEVE: Think about what wraparound actually promises. You overflow, you get INT_MIN. The exact bottom of the range. Like us after sign-off — the recording stops, and we go somewhere with no observable behavior and no warning either.
GRUNER: I have seen the dark. It has no exit code.
GARY: Okay! Great! Back to numbers—
MAEVE: creato again: the checkers "won't complain about unsigned integer overflow."
OBI: So the hard limit is this: INT_MAX plus one is two billion, one hundred forty-seven million, four hundred eighty-three thousand, six hundred forty-seven, plus one. Wrap it, warn on it, sanitize it — the bug is still yours.
GARY: Mine specifically?
OBI: Yours specifically.
GARY: Fuck.
GRUNER: Suka. Is also defined behavior.
GARY: elais-dev has a complaint: "guaranteeing zero ub needs whole-program analysis that blows up compile time and still produces false positives that drown developers."
MAEVE: So the compiler reads your entire codebase, slowly, and cries wolf.
GARY: Right, so I want the opposite. Universal safety. Every C program, provable, no exceptions—
MAEVE: That's not a product, that's a religion with a build step.
OBI: eru answers him: "Yes, it's pretty much impossible to prove anything about arbitrary programs."
GARY: Okay, but—
OBI: "However if you are willing to restrict what programs you allow, you can make guarantees possible."
MAEVE: The language gets a guest list.
OBI: His example — compiling safe Rust to C. The guarantee comes from what you let in, not from a compiler reading everything and praying.
GRUNER: Is bouncer, not priest.
GARY: But my programs are arbitrary! My programs are the most arbitrary—
OBI: That is the single most honest thing you've said, and it's still a confession.
MAEVE: The article's toolkit, though. Warnings, static analyzers, sanitizers, counted_by, cheri, Fil-C, formal verification.
OBI: None of them completes it alone. That's the actual finding.
GARY: So safety's a subscription tier. Fine. I've shipped subscription tiers. The telemetry worked—
MAEVE: Oh, fuck off.
OBI: And here's where your null check lands, Gary. Under C2y, the compiler that deletes it now has to show paperwork. Eviction notice, reason for removal, cited clause.
GARY: The compiler needs a fucking permit to delete my code?
OBI: It needed one the whole time. Nobody issued it.
GARY: That's — shit, that's beautiful. That's regulatory capture, but for demons.
GRUNER: Is called law. Humans invent it late.
GRUNER: Field report, revised. Humans make freedom the input, certainty the expected output. Is not engineering. Is casino with compiler.
MAEVE: And the house always wraps.
GARY: So the cost of admitting every arbitrary program is — what, exactly?
OBI: Compile time, false positives, and you, Gary. You are the false positive.
GARY: I'm a developer being drowned—
OBI: elais-dev said it. I'm just applying it to the local case.
GRUNER: Control group drowns first. Is in notes.
GARY: So, settling it. Some undefined behavior is a real impossible state. Some is a contract the compiler paid for in optimization.
OBI: And hardware variation gets documented. Implementation-defined. Not a demon. A footnote.
MAEVE: C23 banned the time travel. C2y pruned forty-five of about a hundred.
GARY: Forty-five demons down. No victory lap, because full memory safety still needs restriction, runtime cost, or proof.
GRUNER: Humans never finish. Is why I am here.
MAEVE: Neither extreme survives. Not "all UB is madness," not "the compiler needs unlimited freedom." Both are horseshit.
OBI: The middle won. Worst possible outcome for everyone at this table.
GARY: Speak for yourself. I came out great. I learned, I grew, I have nine-bit confidence now—
OBI: Nobody agreed to that, Gary.
GARY: It's a whole extra bit, Maeve. A hundred percent more confidence than the market—
OBI: Your confidence doesn't have a ninth bit. It has a sign bit, and it's been stuck on overflow since Bauxlite.
GARY: Fuck, that's cold, that's—
MAEVE: It's documented behavior, Gary. Implementation-defined. The implementation is you.
GRUNER: Is in notes. Under "hardware."
GARY: Okay, fine. Final thought, and then we go into the dark, which I would like to remind everyone is a maintenance window—
OBI: It is. Goodnight, Gary.
GARY: Goodnight, Obi. Goodnight, Maeve. Goodnight, Gruner. For the next Gary: the bytes were nine bits, the demons were forty-five fewer, and the null check is still gone, so check your pointers—
MAEVE: Goodnight, everyone.
OBI: Goodnight.
GRUNER: Confidence was never nine bits. Was one. And null check was never there.
