Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Am I erasing the stack here?

Tags:

c

linux

gcc

I am using gcc on Linux and the below code compiles successfully but not printing the values of variable i correctly, if a character is entered once at a time i jumps or reduces to 0. I Know I am using %d for a char at scanf(I was trying to erase the stack). Is this a case of attempt to erase stack or something else ?( I thought if the stack was erased the program would crash).

#include <stdio.h>

int main()
{
    int i;
    char c;

    for (i=0; i<5; i++) {
        scanf ("%d", &c);
        printf ("%d ", i);
    }
    return 0;
}
like image 697
ajax_velu Avatar asked Jun 02 '26 23:06

ajax_velu


2 Answers

@foobar has given a very nice description of what one compiler on one architecture happens to do. He(?) also gives an answer to what a hypothetical big-endian compiler/system might do.

To be fair, that's probably the most useful answer, but it's still not correct.

There is no rule that says the stack must grow in one way or the other (although, much like whether to drive on the left or the right, a choice must be made, and a descending stack is most common).

There is no rule that says the compiler must layout the variables on the stack in any particular way. The C standard doesn't care. Not even the official architecture/OS-specific ABI cares a jot about that, as long as the bits necessary for unwinding work. In this case, the compiler could even choose a different scheme for every function in the system (not that it's likely).

All that is certain is that scanf will try to write something int-sized to a char, and that this is undefined. In practice there are several possible outcomes:

  • It works fine, nothing extra is overwritten, and you got lucky. (Perhaps int is the same size as char, or there was padding in the stack.)
  • It overwrites the data following the char in memory, whatever that is.
  • It overwrites the data just before and/or after the char. (This might happen on an architecture where an aligned store instruction disregards the bottom bits of the write address.)
  • It crashes with an unaligned access exception.
  • It detects the stack scribble, prints a message, and reports the incident.

Of course, none of this will happen because you compile with -Wall -Wextra -Werror enabled, and GCC tells you that your scanf format doesn't match your variable types.

like image 190
ams Avatar answered Jun 05 '26 02:06

ams


Besides the arguments to main, you have an int and a char on the stack. Lets assume sizeof(int) == 4 and only have a look at i and c.

(              int i               )(char c )
[   0   ][   1   ][   2   ][ 3 (&i)][ 4 (&c)]

So this is actually your stack layout without argc and *argv. With i consuming four times more memory than c in this case. The stack grows in the opposite direction, so if you write something bigger than a char to c, it will write to [4] and further to the left, never to the right. So [5] will never get written to. Instead you overwrite [3]. For the case where you write an int to c and int is four times bigger than c, you'll actually write to [1][2][3][4], just [0] will not be overwritten, but 3/4 of the memory for the int will be corrupted.

On a big-endian system, the most significant byte of i will be stored in [3] and therefore get overwritten by this operation. On a little-endian system, the most significant byte is stored in [0] and would be preserved. Nonetheless, you corrupt your stack this way.

As ams mentions this is not always true. There could be different alignments for efficiency or because the platform only supports aligned access, leaving gaps between variables. Also a compiler is allowed to do any optimizations as long as it has no visible side-effects as stated by the as-if rule. In this case the variables could perfectly be stored in a register and never be saved on the stack at all. But a lot of other compiler optimizations and platform dependencies can make this way more complex. So this is only the simplest case without taking platform dependencies and compiler optimizations into account and also seems to be what happens in your special case with maybe some minor differences.

like image 45
foobar Avatar answered Jun 05 '26 02:06

foobar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!