5

I know it's possible to overflow ordinary code:

char string[9];

scanf("%s", string).

But is it possible to overflow scanf("%8s", string)? 8 is just an example.

I know "%8s" works like a delimit, but I also notice when I input string longer than 8 chars, the program will terminate due to:

* stack smashing detected *: ./a.out terminated

======= Backtrace: =========

...

Obviously there's a flag that detects stack smashing turned on by GCC by default. Since this is a stack smashing, then my guess is that it is still possible to overflow and execute arbitrary code.

Contrary to normal overflow that mangles the caller of scanf("%s"), if scanf("%8s") can overflow, it will overflow within scanf function so that when scanf try to return, control is gained.

But scanf is a syscall that requires mode-switch (switching from user mode into kernel mode), and internally it will call stuff like read to the stdin etc. So not sure if we can overflow in kernel mode or something..

Comments are welcome!!

UPDATE >>

char string[9] is assumed in the above example. char string[8] in following real code.

The question is really about the seeming conflicting story between safe scanf("%8s") and GCC abortion due to stack smashing.

Simplified code:

void foo(pass some pointer) {
char input[8];
int input_number = 0;

while (1) { // looping console
   printf some info;
   scanf("%8s", input);

   input_number = atoi(input);

   if ((strlen(input) == 1) && (strncmp(input, "q", 1) == 0)) {
       input_number = -1;
   }
   switch (input_number) {
       case -1: to quit the console if input = 'q';
       default: to print info that pointer refers to;
       ...
   } 

}

}

Note:

  1. foo is called by someone else.
  2. Though string is 8 bytes in real code with "%8s", I don't think this lead to smashing.
5
  • scanf is a runtime library function—no mode switch is necessary since it operates in user space, unless it has to request buffer filling, in which case it would call read or fread. Commented Nov 24, 2009 at 5:18
  • 2
    as noted several times in the answers, a nul byte is added, so you need a 9 character buffer to accept up to 8 characters of input. Commented Nov 24, 2009 at 6:18
  • 1
    As plenty of people have pointed out, your assumption in "Note 2." is wrong. That example allows a single byte of overflow, which is what gcc is detecting. Commented Nov 24, 2009 at 6:49
  • 1
    you guys are right. I have tested it with a even simpler program but it somehow didn't crash the last time I tried. Now when I enter "12345678" for string[8] and scanf(%8s) it crashes because of stack smashing! So here's the lesson learned. Smashing doesn't necessarily mean there's a stack overflow attack. Commented Nov 27, 2009 at 18:45
  • Even though the buffer happens to be on the stack in this case, the programming bug is a buffer overflow not a stack overflow. I retagged the question accordingly. Commented Feb 3, 2010 at 5:53

4 Answers 4

9

See http://www.opengroup.org/onlinepubs/009695399/functions/scanf.html:

Each directive is composed of one of the following...An optional non-zero decimal integer that specifies the maximum field width.

s
Matches a sequence of bytes that are not white-space characters. The application shall ensure that the corresponding argument is a pointer to the initial byte of an array of char, signed char, or unsigned char large enough to accept the sequence and a terminating null character code, which shall be added automatically.

So it won't overflow a 9-byte string buffer.

Sign up to request clarification or add additional context in comments.

2 Comments

If it won't overflow, why does GCC telling the Stack Smashing story still?
@Figo: somehow you are not understanding. "%8s" will store up to 9 bytes, so you need a nine character array.
4

Don't ever use scanf (or fscanf for that matter) if you want your input to be robust.

You should be using fgets (or a similarly "protected from buffer overflow" variant) then use sscanf on that.

The main problem with scanf and fscanf is that your file pointer can end up in an indeterminate position if the line is not of the expected format (i.e., if the scanf fails). With the fgets/sscanf method, it's a lot easier to guarantee that you're on a line boundary, without having to use ftell and fseek to move around the file.

Regarding your specific query about whether the buffer will overflow, the C standard has this to say:

... the corresponding argument shall be a pointer to the initial element of a character array large enough to accept the sequence and a terminating null character, which will be added automatically.

So, for a "%8s" format, you need a 9-character array.

I suspect you have some other problem in your code. With a test program:

#include <stdio.h>
int main(int argc, char* argv[]) {
    char x1;
    char a[9];
    char x2;
    x1 = x2 = ' ';
    scanf ("%s",a);
    printf ("[%c] [%s] [%c]\n",x1,a,x2);
    return 0;
}

I get:

pax> ./qq.exe
dfjdhadgha...lghjdfgjhd
[s] [dfjdhadgha...lghjdfgjhd] [ ]
  6 [main] qq 4744 _cygtls::handle_exceptions: Error while dumping state
  (probably corrupted stack)
  Segmentation fault (core dumped)

When I change that same program to use "%8s", I get (for exactly the same input):

pax> ./qq.exe
dfjdhadgha...lghjdfgjhd
[ ] [dfjdhadg] [ ]

7 Comments

Yeah, I know. But right now I'm interested in knowing if scanf(%8s) has the same problem as scanf, as GCC is telling me there's still a stack smashing happening!
Describe "robust"? Give some examples?
@ysth: (1) Get your input as lines. (2) Ensure you get whole lines (\n char at end), error otherwise with "line too long". (3) Use sscanf on line - you can do this as many times as you want on the line without worrying about the underlying file.
I also test a similar simplied code like you did and GCC won't complain about stack smashing.. I have posted orig code and the thing is at run time there will be GCC complain when I input 12345678 (Note in real code char string[8]).
@Figo, you need a 9-character array to store an 8-character string. That's because an 8-character string consist of the 8 characters (obviously) and a null terminator character (not so obvious). If you change it to char string[9], you shouldn't have any problems.
|
1

if string is allocated for less then 8 charters it will certainly overwrite the buffer also scanf will not append a null terminator. But as long as you have enough space in string for your value you should not get an overwright.

4 Comments

It would need at least 9 bytes to not overflow.
Actually I think scanf will put a '\0' at the end. C standard says "a terminating null character, which will be added automatically.", also quoted by paxdiablo
I meant if you didn't also have a space for the null.
If you don't have a space for the null, it will put it there regardless (overwriting some other part of the stack if neccessary).
1

As ysth pointed out, the array should be able to contain the string and the terminating null-character, so using an 8-byte array (especially if it's allocated on the stack, as it is in your code) is very likely to mess it up.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.