UB Catalog: The Important Cases
This chapter catalogs the most important cases of undefined behavior in C. For each, it shows bad code, why it is UB, what the standard says, a safe version, a common misconception, and the compiler consequence.
Why This Matters
This is the reference chapter you return to when debugging "it works in debug but not release" bugs. Knowing the catalog — and the safe alternative for each case — is a core skill for systems and security work.
Prerequisites
c.object.6— definedness classification.
How to Read the Catalog
Each entry uses this structure:
- BAD CODE — the UB.
- WHY — the reason.
- WHAT THE STANDARD SAYS — the rule.
- SAFE VERSION — the fix.
- COMMON MISCONCEPTION — what people wrongly believe.
- COMPILER CONSEQUENCE — what the optimizer may do.
1. Out-of-bounds array access
BAD CODE:
int a[5];
int x = a[5]; /* one past the last valid index 4 */
WHY: The index is outside the array object.
WHAT THE STANDARD SAYS: Array subscripting outside the bounds of the array object is undefined behavior.
SAFE VERSION:
if (i >= 0 && i < 5) { int x = a[i]; }
COMMON MISCONCEPTION: "Reading past the end is fine as long as I don't write." No, reading is also UB.
COMPILER CONSEQUENCE: The compiler may assume i is always in bounds and remove your bounds check, or vectorize the loop past the array.
2. Use-after-free / dangling pointer
BAD CODE:
int *p = malloc(sizeof *p);
free(p);
*p = 5; /* use after free */
WHY: The object's lifetime ended at free.
SAFE VERSION: Do not use p after free; set it to NULL if helpful, and manage ownership (c.mem.ownership).
COMMON MISCONCEPTION: "The value is still there, so it works." The allocator may have reused the memory.
COMPILER CONSEQUENCE: The optimizer may reorder the write to *p with the free or assume the pointer is not used, causing corruption.
3. Double free
BAD CODE:
free(p);
free(p); /* double free */
SAFE VERSION: Track ownership; free exactly once. Set p = NULL after free to catch accidental double-free (though this does not fix all cases).
COMMON MISCONCEPTION: "Freeing NULL is fine, so freeing twice must also be fine." free(NULL) is defined; double-free is not.
4. Null pointer dereference
BAD CODE:
int *p = NULL;
int x = *p;
SAFE VERSION: Check if (p != NULL) before dereferencing.
COMPILER CONSEQUENCE: The compiler may assume p is non-null after a check and remove a later null check.
5. Signed integer overflow
BAD CODE:
int x = INT_MAX + 1;
SAFE VERSION: Use unsigned (wraps), or check before adding.
COMMON MISCONCEPTION: "It wraps around." Unsigned wraps; signed overflow is UB.
6. Invalid shift
BAD CODE:
int x = 1 << 32; /* shift count >= width of int */
SAFE VERSION: Ensure 0 <= n < width; use unsigned types.
7. Division by zero (and INT_MIN / -1)
BAD CODE:
int x = a / 0;
int y = INT_MIN / -1;
SAFE VERSION: Check the divisor before dividing.
8. Uninitialized read (indeterminate value)
BAD CODE:
int x;
printf("%d\n", x);
SAFE VERSION: Initialize before use.
9. Invalid pointer arithmetic
BAD CODE:
int a[5];
int *p = a + 6; /* beyond one-past-the-end */
SAFE VERSION: Stay within [a, a + 5].
10. Strict aliasing violation
BAD CODE:
float f = 1.0f;
int *p = (int *)&f;
int x = *p;
SAFE VERSION: Use memcpy to reinterpret representation.
11. Unsequenced side effects
BAD CODE:
int i = 0;
i = i++;
SAFE VERSION: Sequence the operations; never modify the same object twice without a sequence point.
12. Data race
BAD CODE:
/* two threads write the same non-atomic int */
SAFE VERSION: Use _Atomic, mutexes, or proper synchronization (c.conc.5).
13. Invalid format string
BAD CODE:
printf("%d", 3.14); /* wrong specifier */
SAFE VERSION: Match format specifiers to argument types.
14. Invalid library usage
BAD CODE:
memcpy(dst, src, n); /* overlapping buffers */
SAFE VERSION: Use memmove for overlapping buffers.
What You Should Now Know
- UB means the compiler owes you nothing; it may optimize as if UB never
happens.
- Each UB category has a safe alternative; always use it.
- Sanitizers (UBSan, ASan, TSan) detect many of these at run time.
Related Concepts
c.object.6— definedness classification.c.opt.2— UB-based optimization.c.debug.3— sanitizers.c.sec.1— memory corruption.
References
- ISO/IEC 9899:2018 Annex J.2 (undefined behavior), and the individual clauses
cited in each entry.
Verification
- Each entry's classification follows the C standard.
VERIFIED - Signed overflow, OOB, UAF, double-free, null deref, invalid shift, division
by zero, aliasing violation, data race, unsequenced side effects, and format string mismatch are all UB. VERIFIED
- No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Out-of-bounds access
- [ ] Use-after-free and double free
- [ ] Null dereference
- [ ] Signed overflow
- [ ] Invalid shift and division
- [ ] Uninitialized reads
- [ ] Invalid pointer arithmetic
- [ ] Strict aliasing violation
- [ ] Unsequenced side effects
- [ ] Data races
- [ ] Format string and library misuse
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.ub.catalog | 0 | 7 |