Expert · Bits, undefined behaviour and safety · topic 43 of 60 · 30 min
Undefined behaviour
Explanation
Three kinds of "not fully defined"
| Kind | Meaning | Example |
|---|---|---|
| Implementation-defined | Each compiler/platform chooses and documents the behaviour | sizeof(long); right-shifting a negative int; whether char is signed |
| Unspecified | One of several allowed results; need not be documented | The order in which f() and g() run in f() + g() |
| Undefined | No requirements at all; anything can happen | int overflow, a[10] on int a[10], *NULL |
Common undefined behaviour
int big = INT_MAX; big + 1; /* signed overflow (unsigned overflow is defined: it wraps) */
int a[5]; a[5] = 1; /* out of bounds */
int *p = NULL; *p = 1; /* NULL dereference */
int *q = malloc(4); free(q); *q = 1; /* use after free */
int x; printf("%d", x); /* uninitialised read (an indeterminate value) */
int i = 1; i = i++ + 1; /* unsequenced modification */
int s = 1 << 32; /* shift >= width of int */
printf("%d", 3.0); /* wrong printf format */
int r = INT_MIN / -1; /* overflow in division */Why compilers make it worse
Because the standard says these situations cannot happen in a correct program, optimising compilers are allowed to ASSUME they never happen and transform the code accordingly. The results can be surprising: checks for overflow vanish, loops become infinite, and code runs differently at -O0 and -O2.
int will_overflow(int x) {
return x + 1 < x; /* intended overflow check */
}
/* The compiler reasons: "signed overflow is impossible, so x + 1 < x is always false"
and may compile the function as: return 0; */
/* A correct check, without overflowing: */
int will_overflow_ok(int x) { return x == INT_MAX; }
/* or compute in a wider type, or use __builtin_add_overflow (GCC/Clang), or C23's ckd_add */Finding UB
gcc -std=c17 -Wall -Wextra -g -fsanitize=undefined,address prog.c -o prog && ./prog
prog.c:12:13: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'Habits that avoid UB
- Use unsigned or wider types where values can grow (size_t for sizes, int64_t for big counts), and check before arithmetic that could overflow.
- Check every index against the length and every pointer against NULL.
- Initialise every variable.
- One modification of a variable per expression.
- Match printf formats to argument types (gcc checks these with -Wformat).
- Test with sanitizers and multiple optimisation levels.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Undefined behaviour · 1/6
In this lesson you will learn what undefined behaviour is and why it is dangerous.
Prefer a video by a teacher? Find free videos on “undefined behavior in c explained compiler optimization sanitizer” on YouTube ↗
In real life: An overflow check that the compiler removed
A billing system added a check if (total + amount < total) to detect overflow of an int running total. In a release build with optimisation, the compiler removed the check entirely (signed overflow "cannot happen"), and a very large corporate invoice silently wrapped to a negative number. The fix used a 64-bit total and an explicit limit check before adding.
#include <stdint.h>
int add_amount(int64_t *total, int64_t amount) {
if (amount > 0 && *total > INT64_MAX - amount) return -1; /* would overflow: refuse */
*total += amount;
return 0;
}Practice (0/3 done)
Which is NOT undefined behaviour?
Why might a compiler turn return x + 1 < x; into return 0;?
Write a C program containing five different kinds of undefined behaviour (signed overflow, out-of-bounds read, uninitialised read, shift by 32, use after free). Compile it with -g -fsanitize=undefined,address and record which ones are reported and with what messages. Then fix each one and confirm a clean run.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Secure and defensive CBits, undefined behaviour and safety · same module
- Bitwise operatorsBits, undefined behaviour and safety · same module
- Unsafe Rust: what it is and is notRust Programming · Testing, tooling and unsafe Rust
- Classic C++ pitfalls in contests and coding roundsC++ Programming (modern C++ and DSA) · Competitive programming, interviews and careers
- Sanitizers and warningsDebugging and tools
- Strings are char arraysStrings in C
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does the C standard require when undefined behaviour occurs?
Answer: Nothing at all
No requirements: anything may happen.
Whether plain char is signed is…
Answer: Implementation-defined
Each compiler/platform documents it.
Which tool reports signed overflow at run time?
Answer: -fsanitize=undefined (UBSan)
UBSan.
The order of evaluation of f() and g() in f() + g() is…
Answer: Unspecified
Either may run first.
Which tool runs your tests and detects many kinds of undefined behaviour?Related
Answer: Miri
Miri interprets the program and checks memory rules.
What is a dangling pointer?Related
Answer: A pointer to memory that was freed or went out of scope
Using it is undefined behaviour.
What is a buffer overflow?Related
Answer: Writing past the end of an array into neighbouring memory
It corrupts other data.
Which flag combination helps catch out-of-bounds and undefined behaviour while practising with GCC?Related
Answer: -fsanitize=address,undefined -D_GLIBCXX_DEBUG
Sanitizers and the debug STL report errors at the faulty line.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Bits, undefined behaviour and safety” before its exam: Secure and defensive C, Function pointers and callbacks.
- Module 9 exam: Bits, undefined behaviour and safety · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Data structures in C. 60 topics left in the course before the final exam.