a buffer overflow
Imagine pouring two litres of water into a one-litre jug. The extra litre does not vanish — it spills over the rim onto whatever sits next to the jug. A buffer is a fixed-size container in memory, and a buffer overflow is exactly that spill: writing more data into a buffer than it can hold, so the excess overwrites the memory that happens to lie just beyond it. It is a special, especially dangerous case of out-of-bounds access.
Where the spill lands determines how bad it is. A buffer on the stack sits next to the function's local variables and, further along, the saved return address — the spot the CPU will jump to when the function finishes. Overflow a stack buffer and you can overwrite that return address; the function then 'returns' to wherever the overwritten bytes point. An attacker who controls the overflowing input can aim that jump at code of their choosing. This classic attack is called smashing the stack. A buffer overflow on the heap instead overwrites neighbouring heap allocations or the allocator's own bookkeeping (the size and free-list links stored beside each block), which can corrupt the heap and, with care, also be steered into a takeover. In both cases the immediate symptom may be a crash, a garbage value, or nothing visible at all — the corruption can sit silent until much later.
Why this matters: buffer overflows have been the number-one source of serious security vulnerabilities for decades, precisely because C performs no automatic bounds checking and trusts the programmer to get sizes right. The defenses are layered: at the code level, always bound your copies by the destination size (use snprintf rather than sprintf, and treat any unbounded copy as suspect); at the design level, keep buffer sizes and lengths together and validate input lengths before copying; and at the build level, enable compiler and OS protections (stack canaries, non-executable stack, ASLR) and test with AddressSanitizer, which detects most overflows the moment they happen.
void greet(const char *name) { char buf[16]; strcpy(buf, name); /* if name is longer than 15, OVERFLOW */ printf("Hi %s\n", buf); } /* safer: bound the copy by the destination size */ void greet_safe(const char *name) { char buf[16]; snprintf(buf, sizeof buf, "%s", name); /* never writes past buf */ printf("Hi %s\n", buf); }
strcpy copies until the source's terminator, ignoring the destination size; snprintf bounds the write by sizeof buf and always terminates.
The crash from an overflow is a symptom, often appearing far from the bug, and many overflows do not crash at all — they corrupt silently. 'No crash' is not 'no overflow'; test with AddressSanitizer to surface them.