a format-string vulnerability
When you call printf, the first argument is a format string — a template with placeholders like %d and %s that tells printf how many further arguments to fetch and how to interpret each one. A format-string vulnerability happens when a program lets ATTACKER-controlled text become that format string, so the attacker, not the programmer, decides the placeholders.
Here is why that is so dangerous. printf is variadic: it does not know how many arguments it was actually passed; it simply trusts the format string and walks the call's argument area (registers and stack) pulling out one value per directive. If the attacker supplies a string full of %x, printf happily reads and prints successive words off the stack — an information leak that can dump addresses, defeat ASLR, or reveal a stack canary. More powerful still is %n, a directive that does not PRINT anything but WRITES: it stores the number of characters printed so far into an int that printf expects as the matching pointer argument. By controlling the format string (which sits on the stack as data), an attacker can arrange both the address and, via padding with width specifiers like %100x, the value — turning printf into an arbitrary memory write. The textbook trigger is the omitted format string: printf(user_input) instead of printf("%s", user_input).
It matters because the bug looks utterly harmless — it is just a logging or error-printing line — yet it can yield both an arbitrary read (via leaks) and an arbitrary write (via %n) from a single misused call. The honest framing: the fix is trivial and absolute — always pass a constant format string and put untrusted data in an argument, as in printf("%s", user_input). Modern compilers warn on non-literal formats (gcc -Wformat -Wformat-security), and many libc builds disable or restrict %n in writable format strings, but the discipline is the real defense.
printf(user_input); // BUG: user_input is the format string // if user types "%x %x %x %x" -> leaks four stack words // if user types "...%n" -> writes a value to a chosen address printf("%s", user_input); // FIX: format is constant, data is an argument
The only difference is who controls the format string — and that difference is the whole vulnerability.
%n is the rare printf directive that WRITES rather than prints; even when %n is hardened away, the %x-style read leak alone can be enough to defeat ASLR.