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:

  1. Local variables and arrays (such as the target buffer for gets()).
  2. The saved frame pointer (e.g., EBP or RBP).
  3. 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.