How to prevent scanf causing a buffer overflow in C? How to prevent scanf causing a buffer overflow in C? c c

How to prevent scanf causing a buffer overflow in C?


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.


If you are using gcc, you can use the GNU-extension a specifier to have scanf() allocate memory for you to hold the input:

int main(){  char *str = NULL;  scanf ("%as", &str);  if (str) {      printf("\"%s\"\n", str);      free(str);  }  return 0;}

Edit: As Jonathan pointed out, you should consult the scanf man pages as the specifier might be different (%m) and you might need to enable certain defines when compiling.


Most of the time a combination of fgets and sscanf does the job. The other thing would be to write your own parser, if the input is well formatted. Also note your second example needs a bit of modification to be used safely:

#define LENGTH          42#define str(x)          # x#define xstr(x)         str(x)/* ... */ int nc = scanf("%"xstr(LENGTH)"[^\n]%*[^\n]", array); 

The above discards the input stream upto but not including the newline (\n) character. You will need to add a getchar() to consume this. Also do check if you reached the end-of-stream:

if (!feof(stdin)) { ...

and that's about it.