In the last lesson we saw that memory has no labels. Here is the most famous consequence. A program sets aside a box of a certain size, then copies in data that does not fit. The extra bytes do not vanish. They land in the next boxes along. This is a , and for decades it was one of the most common ways for a stranger's data to take over a program.
One box, too much data
A buffer is a block of memory reserved to hold data, such as the first 8 characters of a name. Picture it as a row of 8 boxes. Here is the classic mistake in C:
#include <string.h>
void greet(const char *name) {
char buf[8]; /* room for 8 bytes */
strcpy(buf, name); /* copies until the text ends. No size check. */
}
strcpy copies bytes until it reaches the end of the text. It never asks how big buf is. If name is 5 characters, fine. If it is 40, the copy keeps writing 32 bytes beyond the end of buf.
Where those bytes land depends on the region from the last lesson. This example uses a local variable, so buf is in a stack frame, and its neighbours are the other variables of that function and the function's bookkeeping.

Why spilling matters
Three outcomes, from mildest to worst:
- A crash. The overwritten neighbour held something the program needed. The process dies. An attacker who can crash a service at will has an availability attack.
- Changed data. The neighbour was an ordinary variable, such as a flag meaning "this user is allowed". Overwrite it and the program's decisions change, with no new code involved.
- Changed control. The stack frame holds the , the note that says where to continue when the function ends. Overwrite it and the function "returns" somewhere the attacker chose. If the attacker can also place their own instructions in memory, they now run code of their choosing inside the victim process. That outcome is when it can be triggered over a network, and it runs with the permissions of the victim process, as the earlier lesson on processes explained.
We will stay at the level of understanding and defending. What matters is the principle: the data the attacker supplies becomes part of the program's control flow because the program trusted its size.
The fix is a size check
The root cause is a missing check, so the root fix is a check:
#include <stdio.h>
void greet(const char *name) {
char buf[8];
snprintf(buf, sizeof buf, "%s", name); /* never writes more than 8 bytes, includes the end marker */
}
snprintf takes the size and stops. The rule that generalises: every copy into a fixed-size box must carry its limit with it, and the limit must come from the box (sizeof buf), not from a guess. This is why older functions without a size argument (strcpy, gets) are treated as red flags in code review.
The best fix is not to have the problem. In Python, Java, Go or Rust, a too-long string simply becomes a longer string or an error. The makes the check for you every time.

Defences that make the bug harder to use
Because so much C and C++ exists, operating systems and compilers add layers that do not remove the bug but raise the price of using it:
- . The compiler places a secret value between the buffer and the return address and checks it before returning. If an overflow changed it, the program stops. Like the canary in a coal mine, it dies first.
- (called DEP or NX). Data regions are marked "never run this as instructions", so supplied data cannot simply be executed.
- . The layout is randomised each run, so an attacker cannot rely on knowing where anything sits.
None of these is a cure. Researchers have a long record of working around each, and a defence that blocks 99 percent of attempts still leaves the bug. Treat them as seatbelts: very worthwhile, but they do not mean you can drive carelessly.
A real case
Read the record's details against what you just learned:
- How it is triggered: crafted data inside an RTF document. The CVSS vector has user interaction set to required, which matches reality: someone has to open the file. That is why phishing with attachments pairs naturally with memory bugs.
- What it gave: the record says arbitrary code execution. The code runs as the user who opened the file, the borrowed-permissions rule again.
- Why it still matters: the CISA Known Exploited Vulnerabilities catalogue lists it, added on 2022-03-03, which means it was known to be used in the wild. A bug from 2010 still being tracked in 2022 is the usual story for old, unpatched software.
- The fix: Microsoft's security bulletin MS10-087, linked from the record. The required action is simply to apply the vendor updates.
The pattern to take away is a parser handling untrusted input and trusting a size inside it. File formats, network messages and image decoders are the classic homes of these bugs, because they all read lengths from data the sender controls.
How AI changes this
Attackers. Hunting for overflows means tracing lengths from input to copy. AI models can read large codebases for this pattern and can help turn a crash into a working exploit faster. Beam's AI-led attacks write-up describes AI models being used to find bugs; what is new is the speed and the lower skill needed, while the bug class itself is unchanged.
Defenders. The same ability helps in review: ask a model to find every copy that lacks a size limit, then verify each result yourself, since models can both miss real cases and flag safe ones. Pair it with tools that do not guess: compiler warnings, fuzzers and the sanitizer in the lab below catch overflows with certainty.
Agents. Agents are asked to "fix the crash" or "port this to Rust". Both are good uses, but a quick fix that silences the symptom without adding the size check leaves the bug in place. Also, an agent that opens untrusted files or web pages relies on parsers like these underneath it, so keep the tools and runtimes it uses patched.
Try it
This detects an overflow on your own machine. It is safe: the program only crashes, and the sanitizer reports exactly where. Save as overflow.c:
#include <string.h>
#include <stdio.h>
int main(int argc, char **argv) {
char buf[8];
if (argc < 2) return 1;
strcpy(buf, argv[1]); /* the bug */
printf("copied: %s\n", buf);
return 0;
}
cc -fsanitize=address -g overflow.c -o overflow
./overflow hi
./overflow "this text is much longer than eight bytes"
The short input works. The long input stops with an AddressSanitizer report saying stack-buffer-overflow, the line number, and which variable was overrun. Now change strcpy(buf, argv[1]); to snprintf(buf, sizeof buf, "%s", argv[1]);, rebuild, and run the long input again. The report disappears and the text is quietly cut to fit.
Check yourself
Why is strcpy considered dangerous but snprintf with a size is not?
strcpy copies until the source ends, with no knowledge of the destination's size, so the sender's data decides how much gets written. snprintf takes the destination size and never writes beyond it.
A stack canary is in place. Does that mean the overflow bug is fixed?
No. The canary only notices damage after the fact and stops the program, usually turning a takeover into a crash. The unchecked copy is still there, so the service can still be crashed, and some overflows do not touch the canary at all.
CVE-2010-3333 needed someone to open a file. What does that tell you about who the real attacker has to fool?
The user. The bug is in the parser, but the delivery relies on a person opening a document, which is why attachment handling and user awareness matter alongside patching.
Next
Overflowing a box is one way to break memory. The other is using a box after you have handed it back. That is next.

