How to prevent scanf causing a buffer overflow in C?

goe picture goe · Oct 25, 2009 · Viewed 99.6k times · Source

I use this code:

while ( scanf("%s", buf) == 1 ){

What would be the best way to prevent possible buffer overflow so that it can be passed strings of random lengths?

I know I can limit the input string by calling for example:

while ( scanf("%20s", buf) == 1 ){

But I'd prefer to be able to process whatever the user inputs. Or can't this be done safely using scanf and I should use fgets?

Answer

Jonathan Leffler picture Jonathan Leffler · Oct 25, 2009

In their book The Practice of Programming (which is well worth reading), Kernighan and Pike discuss this problem, and they solve it by using snprintf() to create the string with the correct buffer size for passing to the scanf() family of functions. In effect:

int scanner(const char *data, char *buffer, size_t buflen)
{
    char format[32];
    if (buflen == 0)
        return 0;
    snprintf(format, sizeof(format), "%%%ds", (int)(buflen-1));
    return sscanf(data, format, buffer);
}

Note, this still limits the input to the size provided as 'buffer'. If you need more space, then you have to do memory allocation, or use a non-standard library function that does the memory allocation for you.


Note that the POSIX 2008 (2013) version of the scanf() family of functions supports a format modifier m (an assignment-allocation character) for string inputs (%s, %c, %[). Instead of taking a char * argument, it takes a char ** argument, and it allocates the necessary space for the value it reads:

char *buffer = 0;
if (sscanf(data, "%ms", &buffer) == 1)
{
    printf("String is: <<%s>>\n", buffer);
    free(buffer);
}

If the sscanf() function fails to satisfy all the conversion specifications, then all the memory it allocated for %ms-like conversions is freed before the function returns.