Frontend Labs
⋯

Part V — Security-focused C

A different angle on everything so far: not building features, but not shooting yourself in the foot. Undefined behavior is the one topic every C programmer needs memorized rather than just referenced — the rest of this Part (safe string handling, hardening flags, fuzzing, static analysis) is the tooling that catches what memorization alone misses.

28. Undefined behavior: the list every C programmer must memorize

Undefined behavior (UB) means the standard places no requirement on what happens — not "it crashes," but "the compiler is allowed to assume it never happens and optimize accordingly," which is often more surprising than a crash.

UB Example Why it matters
Signed integer overflow INT_MAX + 1 Compiler may assume x + 1 > x always and eliminate an overflow check you wrote
Out-of-bounds access arr[10] on a 10-element array (valid indices 0–9) No bounds checking exists; you silently corrupt adjacent memory
Use of uninitialized value int x; printf("%d", x); Value is indeterminate; sanitizers catch it, plain runs may "work" until they don't
NULL pointer dereference *(int*)NULL Often segfaults, but the compiler may also assume it's unreachable and delete surrounding code
Use-after-free / dangling pointer Section 10.2 Silent corruption, sometimes exploitable
Data race Section 20.1 No defined result; can tear values, reorder observably
Modifying a const object casting away const and writing May write to read-only memory (segfault) or silently do nothing, depending on placement
Missing return in non-void function falls off the end Caller gets a garbage value
Strict aliasing violation reading a float* through an int* Compiler may reorder/omit the access assuming the pointers can't alias
Shift by ≥ the type's width 1 << 32 on a 32-bit int Undefined behavior, not "0" as many assume

War story. The signed-overflow row above is not theoretical: a real class of Linux kernel bugs came from code like if (ptr + len < ptr) to detect pointer-arithmetic overflow — because pointer overflow (and signed overflow generally) is UB, compilers began optimizing this entire check away, reasoning "overflow can't happen, so this comparison is always false." Code that had defended against overflow for years silently stopped defending against it after a compiler upgrade, with no warning. The fix across the industry was to switch to __builtin_add_overflow (GCC/Clang builtin) or unsigned arithmetic with explicit wraparound semantics, both of which are defined behavior the compiler cannot optimize away.

#include <stdio.h>

int safe_add(int a, int b, int *result) {
    return __builtin_add_overflow(a, b, result);  // returns 1 if overflow occurred, defined behavior
}

int main(void) {
    int result;
    if (safe_add(2147483647, 1, &result)) {
        printf("overflow detected, not UB\n");
    }
    return 0;
}

Generated from real-life-c-guide.md · 8 Oct 2026 16:36