tags:

views:

561

answers:

5

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?

+3  A: 

Directly using scanf(3) and its variants poses a number of problems. Typically, users and non-interactive use cases are defined in terms of lines of input. It's rare to see a case where, if enough objects are not found, more lines will solve the problem, yet that's the default mode for scanf. (If a user didn't know to enter a number on the first line, a second and third line will probably not help.)

At least if you fgets(3) you know how many input lines your program will need, and you won't have any buffer overflows...

DigitalRoss
+1  A: 

Limiting the length of the input is definitely easier. You could accept an arbitrarily-long input by using a loop, reading in a bit at a time, re-allocating space for the string as necessary...

But that's a lot of work, so most C programmers just chop off the input at some arbitrary length. I suppose you know this already, but using fgets() isn't going to allow you to accept arbitrary amounts of text - you're still going to need to set a limit.

Mark Bessey
So does anybody know how to do that with scanf then?
goe
Using fgets in a loop can allow you to accept arbitrary amounts of text - just keep `realloc()`ing your buffer.
bdonlan
A: 

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.

dirkgently
+1  A: 

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;
}
John Ledbetter
That's more an issue of using glibc (the GNU C Library) than of using the GNU C Compiler.
Jonathan Leffler
+1  A: 

In their book 'The Practice of Programming' (which is well worth reading), Kernighan and Pike discuss this problem, and they solve it by using sprintf() 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];
    snprintf(format, sizeof(format), "%%%ds", (int)buflen);
    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.

Jonathan Leffler