Why gets Is Vulnerable to Buffer Overflow in C
The standard C library function gets() is historically
one of the most hazardous functions in software development due to its
complete lack of bounds checking. This article examines the
architectural flaws of gets(), demonstrates how it enables
buffer overflow attacks that can compromise program control flow, and
highlights the secure alternatives that replaced it in modern C
standards.
The Fundamental Design Flaw
The signature of the function is defined as
char *gets(char *str);. It accepts a single argument: a
pointer to the character array where the input string should be
stored.
The fatal vulnerability lies in what the function signature omits: a
parameter specifying the maximum size of the destination buffer. When
invoked, gets() reads characters from standard input
(stdin) until it encounters a newline character
(\n) or an end-of-file (EOF) indicator, appending a null
terminator (\0) at the end. Because the function is
entirely unaware of the target buffer's allocated capacity, it will
continuously write incoming bytes to adjacent memory addresses if the
user input exceeds the size of the array.
How Buffer Overflow Occurs on the Stack
In typical C execution environments, local variables are allocated on the call stack. A stack frame generally contains:
- Local variables and arrays (such as the target buffer for
gets()). - The saved frame pointer (e.g.,
EBPorRBP). - The return address pointing to the instruction that should execute once the current function completes.
Because stack buffers grow toward higher memory addresses (toward the saved frame pointer and return address) on most standard architectures, writing past the end of an array immediately corrupts the surrounding stack frame.
When an input string longer than the allocated space is supplied to
gets():
- Memory Corruption: Excess bytes overwrite other local variables located after the buffer.
- Denial of Service: Overwriting the return address with random or arbitrary data causes the program to jump to invalid memory upon returning, resulting in a segmentation fault and application crash.
- Arbitrary Code Execution: An attacker can carefully craft an input payload containing machine code (shellcode) or Return-Oriented Programming (ROP) gadgets, precisely padding the string so that the function's return address is overwritten with the address of the malicious payload. When the function returns, the CPU executes the attacker's instructions with the privileges of the running process.
Deprecation and Removal
Because the vulnerability is inherent to its parameter list,
gets() cannot be used safely under any circumstances where
input length cannot be guaranteed at compile time.
Due to these unfixable security implications, the ISO C standards
committee formally deprecated gets() in the C99 standard
and removed it entirely from the language in the C11 standard. Modern
compilers will issue severe warnings or refuse to link programs that
attempt to call it.
Secure Alternatives
Developers must use input functions that enforce explicit buffer boundaries:
fgets(): The standard replacement for reading lines. It requires the developer to specify the maximum number of characters to copy, preventing overflow:char buffer[64]; if (fgets(buffer, sizeof(buffer), stdin) != NULL) { // Input is safely bounded to 63 characters plus the null terminator }getline(): A POSIX standard function that automatically resizes or allocates the target buffer dynamically to accommodate inputs of arbitrary length.gets_s(): Introduced in the optional Annex K of the C11 standard, this function requires a buffer size parameter and aborts execution if an overflow is attempted.