πŸš€ UllrichLumina

Why is the gets function so dangerous that it should not be used

Why is the gets function so dangerous that it should not be used

πŸ“… | πŸ“‚ Category: Programming

The gets() function, once a staple in C programming, is now infamous for its inherent insecurity. It’s a function so dangerous that its use is universally condemned, and for good reason. This post delves into why gets() is considered a security risk, exploring its vulnerabilities and offering safer alternatives for reading input in your C programs. Understanding this crucial aspect of C programming can significantly enhance your code’s security and prevent potential exploits.

The Buffer Overflow Vulnerability

The core issue with gets() lies in its inability to handle buffer overflows. This function reads input from the standard input (stdin) and stores it into a character array provided by the programmer. Crucially, gets() doesn’t check the size of the input against the allocated buffer size. This creates a dangerous scenario: if the input exceeds the buffer’s capacity, it overwrites adjacent memory regions, leading to unpredictable behavior and potential security breaches. This vulnerability makes programs using gets() susceptible to buffer overflow attacks, allowing malicious actors to inject and execute arbitrary code.

Imagine a program allocating a buffer of 10 bytes. If a user inputs 20 bytes, gets() will happily write beyond the allocated space, corrupting other data in memory. This could lead to program crashes, incorrect data manipulation, or even allow attackers to take control of the system.

Real-World Implications of the gets() Vulnerability

The consequences of using gets() are not merely theoretical. History is littered with examples of exploits leveraging this vulnerability. The infamous Morris Worm, one of the earliest internet worms, exploited a buffer overflow in the fingerd daemon, partially due to its use of gets(). This incident highlighted the devastating potential of unchecked input, crippling a significant portion of the early internet. This serves as a stark reminder of how seemingly small coding errors can have far-reaching consequences.

More recent examples continue to surface, demonstrating that buffer overflows remain a relevant threat. Even today, legacy systems or poorly written code can fall victim to these exploits, emphasizing the importance of secure input handling.

Safer Alternatives to gets()

Fortunately, safer alternatives to gets() exist, offering robust input handling and preventing buffer overflows. The fgets() function is the recommended replacement. Unlike gets(), fgets() takes an argument specifying the maximum number of characters to read, preventing it from writing beyond the allocated buffer. This crucial difference makes fgets() a significantly safer option.

  • fgets(buffer, size, stdin); This line of code reads at most size - 1 characters from stdin and stores them into buffer. It also adds a null terminator to ensure proper string termination.
  • Other alternatives include more advanced input functions that provide additional control and validation, but fgets() is generally sufficient for most cases.

Here’s a simple comparison:

  1. gets(): Unsafe, no buffer size control.
  2. fgets(): Safe, includes buffer size control.

Best Practices for Secure Input Handling

Beyond simply replacing gets() with fgets(), adopting secure coding practices is essential. Always validate user input, checking its length, format, and content. Sanitize input to remove potentially harmful characters or sequences. Employing these practices creates a robust defense against a wide range of input-related vulnerabilities.

Consider using static analysis tools to identify potential buffer overflows and other security flaws in your code. These tools can automatically analyze your codebase and flag potentially problematic areas, helping you catch vulnerabilities before they become exploitable. Regular code reviews and security audits are also vital for maintaining a secure codebase.

Frequently Asked Questions

Q: Is gets() ever safe to use?

A: No, gets() is inherently unsafe and should never be used in any code. There’s always a risk of buffer overflow, regardless of the intended input.

Switching to safer functions like fgets() and embracing secure coding practices is paramount. By understanding the dangers of gets() and adopting these alternatives, you can create more secure and reliable C programs. Check out this article for more security tips. Further research on secure coding practices in C will greatly benefit any developer. Explore resources like the CERT C Secure Coding Standard (external link here) and OWASP (external link here) for in-depth guidance. Another valuable resource is the book “Secure Coding in C and C++” (external link here). Investing in these practices strengthens your code, protects users, and contributes to a more secure digital environment.

Question & Answer :
When I try to compile C code that uses the gets() function with GCC, I get this warning:

(.text+0x34): warning: the `gets’ function is dangerous and should not be used.

I remember this has something to do with stack protection and security, but I’m not sure exactly why.

How can I remove this warning and why is there such a warning about using gets()?

If gets() is so dangerous then why can’t we remove it?

Why is gets() dangerous

The first internet worm (the Morris Internet Worm) escaped about 30 years ago (1988-11-02), and it used gets() and a buffer overflow as one of its methods of propagating from system to system. The basic problem is that the function doesn’t know how big the buffer is, so it continues reading until it finds a newline or encounters EOF, and may overflow the bounds of the buffer it was given.

You should forget you ever heard that gets() existed.

The C11 standard ISO/IEC 9899:2011 eliminated gets() as a standard function, which is A Good Thingβ„’ (it was formally marked as ‘obsolescent’ and ‘deprecated’ in ISO/IEC 9899:1999/Cor.3:2007 β€” Technical Corrigendum 3 for C99, and then removed in C11). Sadly, it will remain in libraries for many years (meaning ‘decades’) for reasons of backwards compatibility. If it were up to me, the implementation of gets() would become:

char *gets(char *buffer) { assert(buffer != 0); abort(); return 0; } 

Given that your code will crash anyway, sooner or later, it is better to head the trouble off sooner rather than later. I’d be prepared to add an error message:

fputs("obsolete and dangerous function gets() called\n", stderr); 

Modern versions of the Linux compilation system generates warnings if you link gets() β€” and also for some other functions that also have security problems (mktemp(), …).

Alternatives to gets()

fgets()

As everyone else said, the canonical alternative to gets() is fgets() specifying stdin as the file stream.

char buffer[BUFSIZ]; while (fgets(buffer, sizeof(buffer), stdin) != 0) { ...process line of data... } 

What no-one else yet mentioned is that gets() does not include the newline but fgets() does. So, you might need to use a wrapper around fgets() that deletes the newline:

char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp) { if (fgets(buffer, buflen, fp) != 0) { size_t len = strlen(buffer); if (len > 0 && buffer[len-1] == '\n') buffer[len-1] = '\0'; return buffer; } return 0; } 

Or, better:

char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp) { if (fgets(buffer, buflen, fp) != 0) { buffer[strcspn(buffer, "\n")] = '\0'; return buffer; } return 0; } 

Also, as caf points out in a comment and paxdiablo shows in their answer, with fgets() you might have data left over on a line. My wrapper code leaves that data to be read next time; you can readily modify it to gobble the rest of the line of data if you prefer:

if (len > 0 && buffer[len-1] == '\n') buffer[len-1] = '\0'; else { int ch; while ((ch = getc(fp)) != EOF && ch != '\n') ; } 

The residual problem is how to report the three different result states β€” EOF or error, line read and not truncated, and partial line read but data was truncated.

This problem doesn’t arise with gets() because it doesn’t know where your buffer ends and merrily tramples beyond the end, wreaking havoc on your beautifully tended memory layout, often messing up the return stack (a Stack Overflow) if the buffer is allocated on the stack, or trampling over the control information if the buffer is dynamically allocated, or copying data over other precious global (or module) variables if the buffer is statically allocated. None of these is a good idea β€” they epitomize the phrase ‘undefined behaviour`.


There is also the TR 24731-1 (Technical Report from the C Standard Committee) which provides safer alternatives to a variety of functions, including gets():

Β§6.5.4.1 The gets_s function

###Synopsis

#define __STDC_WANT_LIB_EXT1__ 1 #include <stdio.h> char *gets_s(char *s, rsize_t n); 

Runtime-constraints

s shall not be a null pointer. n shall neither be equal to zero nor be greater than RSIZE_MAX. A new-line character, end-of-file, or read error shall occur within reading n-1 characters from stdin.25)

3 If there is a runtime-constraint violation, s[0] is set to the null character, and characters are read and discarded from stdin until a new-line character is read, or end-of-file or a read error occurs.

Description

4 The gets_s function reads at most one less than the number of characters specified by n from the stream pointed to by stdin, into the array pointed to by s. No additional characters are read after a new-line character (which is discarded) or after end-of-file. The discarded new-line character does not count towards number of characters read. A null character is written immediately after the last character read into the array.

5 If end-of-file is encountered and no characters have been read into the array, or if a read error occurs during the operation, then s[0] is set to the null character, and the other elements of s take unspecified values.

6 The fgets function allows properly-written programs to safely process input lines too long to store in the result array. In general this requires that callers of fgets pay attention to the presence or absence of a new-line character in the result array. Consider using fgets (along with any needed processing based on new-line characters) instead of gets_s.

25) The gets_s function, unlike gets, makes it a runtime-constraint violation for a line of input to overflow the buffer to store it. Unlike fgets, gets_s maintains a one-to-one relationship between input lines and successful calls to gets_s. Programs that use gets expect such a relationship.

The Microsoft Visual Studio compilers implement an approximation to the TR 24731-1 standard, but there are differences between the signatures implemented by Microsoft and those in the TR.

The C11 standard, ISO/IEC 9899-2011, includes TR24731 in Annex K as an optional part of the library. Unfortunately, it is seldom implemented on Unix-like systems.


getline() β€” POSIX

POSIX 2008 also provides a safe alternative to gets() called getline(). It allocates space for the line dynamically, so you end up needing to free it. It removes the limitation on line length, therefore. It also returns the length of the data that was read, or -1 (and not EOF!), which means that null bytes in the input can be handled reliably. There is also a ‘choose your own single-character delimiter’ variation called getdelim(); this can be useful if you are dealing with the output from find -print0 where the ends of the file names are marked with an ASCII NUL '\0' character, for example.