Click to See Complete Forum and Search --> : seg fault
wittysaint
04-18-2002, 06:10 AM
hi there,
why doesnt the following code seg fault??
int main()
{
char t[2];
strcpy(t,"fooba");
printf("%s",t);
return 0;
}
any idea??
tia,
cor
Strogian
04-18-2002, 06:44 AM
t can only hold 2 characters, and you're trying to copy 5 into it.
EDIT: Oh, you said why DOESN'T it segfault... Hehe.. I was wondering why it seemed like so obvious an answer. :)
Anyway, I believe it doesn't segfault because of where t is in memory. Segmentation faults don't occur just when you do anything you're not supposed to. They only happen when you try to write to read-only memory. (and maybe also if you try to read from restricted memory, if such a thing exists) So apparently there was still writable memory after the two characters defined for t. I believe this space is called the data segment, but don't quote me on that. ;) If someone could actually say exactly what the memory looks like, that would be cool. All I can say is what I think, based on the observations. I don't know why there is still read-write memory after t though, since that's the ONLY variable defined in the program. Maybe it has to do with argc and argv or something...
[ 18 April 2002: Message edited by: Strogian ]
wittysaint
04-18-2002, 07:42 AM
still clue - less :confused:
bwkaz
04-18-2002, 09:15 AM
char t[2];
allocates space for 2 characters on the stack. The same stack where you put return addresses, parameters (on x86 anyway), and other local variables.
Then, the strcpy line copies 5 characters into that space. Since only two characters were allocated, you overwrote either 3 bytes of other local variables, 3 bytes out of the 4 for the return address, or one of the parameters to the function.
But the protection mechanism of x86 processors (x >= 3) won't by default detect this, and SIGSEGV signals are based on that protection mechanism. It will only trap writes to read-only memory, or any access past what's been allocated (with the exception of stuff on the stack, because that's too hard to track).
Strogian
04-18-2002, 03:50 PM
Ah, so it IS the stack. I had a sneaking suspicion that that was it, but wasn't sure. ;)
wittysaint
04-19-2002, 12:06 AM
mm., just what i thought ;) , but then this happened, on copying foobar (adding one char) it seg faults!!.
so how did this happen??.
and OTOH, if i had over-written 3 bytes of the return addr, then how did the return tke place??, since the addr is now corrupted, and i dont have other local variables here and AFAIK no arguments to the functions.
tia
:confused:
[ 19 April 2002: Message edited by: wittysaint ]
bwkaz
04-19-2002, 08:37 AM
Originally posted by wittysaint:
<STRONG>and OTOH, if i had over-written 3 bytes of the return addr, then how did the return tke place??, since the addr is now corrupted, and i dont have other local variables here and AFAIK no arguments to the functions.</STRONG>
Errr... you've got me there.
Maybe it has something to do with byte-alignment? Depending on the alignment your compiler picks, those 2 characters may take 2 bytes, 4 bytes, or even 8 bytes of stack.
Modern Intel (i.e. post-486) processors have a flag that the OS can set so that it traps on any unaligned memory access (this is for speed -- an unaligned memory access takes a lot longer for the hardware to do), which will make compilers that target that OS try to align everything as well as they can. I know for a fact that the way Intel processors use the stack, if you try to put one byte on it, you'll actually allocate 4 bytes. So maybe those two bytes somehow (and I have no idea how...) got changed into 8?
But then the six-character thing shouldn't have segfaulted....
I don't know.
Stuka
04-19-2002, 02:21 PM
Memory for local variables comes AFTER the return address on the stack. Most likely the compiler generated a program stack of a given size based on the code (you could use gcc -S (I think that's the flag) to see how big it is). It just so happened that "fooba" was small enough to fit in the allocated stack space (in memory), but "foobar" exceeded that amount, causing the segfault. I'm not 100% sure this is what happened, but it does fit the facts of the matter...
marvin
04-19-2002, 07:52 PM
Yes as Stuka said, you have to look at the assembly code to understand what exactly happens.
On my system (RH 7.2 with gcc 2.96 on an old i586) the stack will look like described below just before the call to strcpy. I compiled without any optimisation, debugging code etc.
| ... |
0x100c | &argv[1] |
0x1008 | &argv[0] |
0x1004 | argc |
0x1000 | Ret. addr. |
EBP -> 0x0ffc | Old_EBP |
0x0ff8 | Uninit data | Reserved space for local variables
0x0ff4 | -"- | Reserved space for local variables
0x0ff0 | -"- | Reserved space for local variables
0x0fec | -"- | Reserved space for local variables
0x0fe8 | &fooba | Arg 2 to strcpy (source string)
ESP -> 0x0fe4 | 0x0ffa | Arg 1 to strcpy (destination string)
&fooba is the address to the string "fooba" which is somewhere in the memory. I used the address 0x1000 as the initial value of the stack pointer (ESP) when the main function is entered. (The virtual address will most likely be something else, I just use it here so I can refer to the addresses to elements on the stack as "0x0ffc" instead of "INITIAL_ESP - 0x4")
strcpy is told to copy data from &fooba to 0x0ffa. strcpy will copy data until it finds a NULL char, so in this case 6 bytes will be copied, 'f', 'o', 'o', 'b', 'a' and 0. Thus, the stack will look like
| ... |
0x100c | &argv[1] |
0x1008 | &argv[0] |
0x1004 | argc |
0x1000 | Ret. addr. |
EBP -> 0x0ffc | 0x0061 626f | The bytes 'o' 'b' 'a' '\0'
0x0ff8 | 0x6f66 ???? | Two random bytes and 'f' and 'o'
0x0ff4 | Uninit data | Reserved space for local variables
0x0ff0 | -"- | Reserved space for local variables
0x0fec | -"- | Reserved space for local variables
0x0fe8 | &fooba | Arg 2 to strcpy (source string)
ESP -> 0x0fe4 | 0x0ffa | Arg 1 to strcpy (destination string)
after the call to strcpy. In this case strcpy did overwrite the old EBP pointer (this was saved at the start of main and is supposed to be restored when the main function returns) but the glibc stuff that cleans up and exits from the program does not seem to care... Note that the return address is not modified.
If your string was a single byte longer it would have messed with the return address and, most likely, cause a segmentation fault. That, of course depends on what the return address was overwritten with, and in this case what data the string have. On my system, changing the code to
#include <string.h>
#include <stdio.h>
unsigned char fooba[] = { 'f', 'o', 'o', 'b', 'a', 'r', 0x90, 0x84, 0x04, 0x08, 0x00 };
int main()
{
char t[2];
strcpy(t, fooba);
printf("%s",t);
return 0;
}
causes the return address to be overwritten by the address to the main function. This makes the program loop and print foobar + some garbage in every loop. However, ESP increases by 0x4 in every loop so after a while it segfaults when ESP points to something outside the stack.