Never Use gets To Read A Line

Updated:

TL;DR

gets is a deprecated function which can cause buffer overflows, use fgets insted.

The Morris worm

On November 2, 1988, the Morris worm, one of the first computer worms distributed over the internet, was launched from MIT computers. It was estimated that it caused several million dollars in damage, and it was the first case of its kind to gain massive media attention. The Morris worm exploited a buffer overflow caused by gets to spread the infection.

Why is gets so dangerous?

The gets prototype suggests the answer.

char* gets(char* str);

As you can learn more about here, gets reads chars from the standard input and stores them into str, until a newline character or the end-of-file is reached. It returns the str address on success, a NULL pointer otherwise.

We mentioned a buffer overflow exploit earlier. It doesn’t take a lot of imagination to understand that a function that reads from stdin until it finds a terminator can cause a buffer overflow. There is nothing to prevent gets from continuing to read from stdin indefinitely, or, at least, longer than the capacity of the destination buffer.

So, you have to make sure that a safer version of gets stops when you run out of space.

Safer alternatives to gets

You can use fgets instead of gets.

char *fgets(char *str, int count, FILE *stream);

You can study all the details here, however what matters most to us now is that with fgets you can specify the size of your memory buffer where to store the data. Just use the count argument correctly and you no longer have to worry about any buffer overflows.

char buffer[BUF_SIZE];

while (fgets(buffer, sizeof(buffer), stdin) != 0) {
    /* Process a line... */
}

The only annoyance with fgets is that buffer may still not be large enough to store an entire line. Don’t worry, no more buffer overflows, however in your code you still have to deal with the fact that an acquisition of a single line could be split into multiple fgets calls.

If you are programming on a POSIX system, you can also solve this nuisance by using getline. This function dynamically allocates a memory chunk large enough to accommodate the entire line. It’s super handy. You don’t even have to think about how to manage the sizing of the memory buffer anymore. Although, of course, there is a cost to all of this. It uses dynamically allocated memory, so you don’t have to forget to free it. Otherwise, you will cause memory leaks. And also, as already mentioned, getline can only be found on POSIX systems. So, not for Windows users.

Is gets still here with us?

The C11 standard has finally eliminated gets from the standard functions. However, for backward compatibility reasons, it will remain in most C libraries for a long time to come. You still have to be careful, but nowadays, many compilers at least print a warning if you inadvertently try to use it.