Format argument is not present
| Vulnerability potential | Medium |
| DDoS potential | Low |
Format defines a parameter which is not present in argument string
Impact
When a conversion specification in the format string has no matching argument
(e.g. printf("%d %d\n", x)), the formatting function still tries to fetch a
value for it. It reads whatever happens to occupy the next argument slot — a
scratch register or a stack location that was never set up for this call. The
fetched value is indeterminate, so at best the output is garbage. At worst, a
pointer-consuming specifier such as %s dereferences an arbitrary address and
prints memory until it hits a NUL byte, or %n writes through a bogus
pointer. The C standard makes the whole call undefined behavior, so the range of
outcomes runs from silently wrong output to an immediate crash.
Vulnerability potential
This issue has a clear security dimension whenever the missing argument feeds a pointer-consuming specifier.
%s(and%ls) with a missing argument reads an indeterminate pointer and dumps whatever memory it points at, which can leak stack contents, heap addresses, stack canaries or other secrets.%nwith a missing argument writes the running output count through an uncontrolled pointer, corrupting memory and potentially enabling control-flow hijack.- Dereferencing the indeterminate pointer frequently faults, terminating the process and contributing to denial of service.
Technical details
C variadic functions carry no information about how many arguments were actually
passed; the callee trusts the format string to tell it. printf walks the
format, and for each conversion it issues a va_arg read of the requested
type. va_arg simply advances through the argument area defined by the
platform ABI — there is no bounds check and no end marker.
On the x86-64 System V ABI the first integer/pointer arguments arrive in
registers (rdi, rsi, rdx, rcx, r8, r9) and the rest on
the stack; a missing argument therefore yields a leftover register or an
arbitrary stack word. The exact garbage value, and whether the call crashes,
depends on register/stack state at the call site, so the defect is highly
non-deterministic and may pass testing yet fail in production.
Catching the issue
Compilers
GCC and Clang diagnose the mismatch at compile time with -Wformat (enabled
by -Wall); -Wformat=2 is stricter. For your own variadic wrappers,
annotate them with __attribute__((format(printf, n, m))) so the same checks
apply. Promote the warning to an error with -Werror=format.
Static and dynamic analysis
Clang-Tidy, Coverity, PVS-Studio and PC-lint flag format/argument count
mismatches. At runtime, glibc’s _FORTIFY_SOURCE adds some checking, and
AddressSanitizer may catch the resulting bad dereference, though neither
reliably detects a plain missing scalar argument.
How to reproduce
Build with warnings on (-Wformat) to see the diagnostic; run to observe
garbage output, or a crash on the %s line.
#include <stdio.h>
int main(void)
{
int x = 42;
/* Two conversions, one argument: the second %d reads garbage. */
printf("%d %d\n", x);
/* %s with no argument dereferences an indeterminate pointer. */
printf("%s\n");
return 0;
}