Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I not getting a segfault error with this simple code?

I have to show an error when I access an item outside of an array (without creating my own function for it). So I just thought it was necessary to access the value out of the array to trigger a segfault but this code does not crash at all:

int main(){
    int tab[4];
    printf("%d", tab[7]);
}

Why I can't get an error when I'm doing this?

like image 870
Adraqi Avatar asked Dec 18 '22 10:12

Adraqi


2 Answers

When you invoke undefined behavior, anything can happen. You program may crash, it may display strange results, or it may appear to work properly.

Also, making a seemingly unrelated change such as adding an unused local variable or a simple call to printf can change the way in which undefined behavior manifests itself.

When I ran this program, it completed and printed 63. If I changed the referenced index from 7 to 7000, I get a segfault.

In short, just because the program can crash doesn't mean it will.

like image 117
dbush Avatar answered Dec 20 '22 23:12

dbush


Because the behavior when you do things not allowed by the spec is "undefined". And because there are no bounds checks required in C. You got "lucky".

int tab[4]; says to allocate memory for 4 integers on the stack. tab is just a number of a memory address. It doesn't know anything about what it's pointing at or how much space as been allocated.

printf("%d", tab[7]); says to print out the 8th element of tab. So the compiler does...

  • tab is set to 1000 (for example) meaning memory address 1000.
  • tab represents a list of int, so each element will be sizeof(int), probably 4 or 8 bytes. Let's say 8.
  • Therefore tab[7] means to start reading at memory position (7 * 8) + 1000 = 1056 and for 8 more bytes. So it reads 1056 to 1063.

That's it. No bounds checks by the program itself. The hardware or OS might do a bounds check to prevent one process from reading arbitrary memory, have a look into protected memory, but nothing required by C.

So tab[7] faithfully reproduces whatever garbage is in 1056 to 1063.

You can write a little program to see this.

int main(){
  int tab[4];
  printf("sizeof(int): %zu\n", sizeof(int));
  printf("tab: %d\n", tab);
  printf("&tab[7]: %d\n", &tab[7]);

  /* Note: tab must be cast to an integer else C will do pointer
     math on it. `7 + tab` gives the same result. */
  printf("(7 * sizeof(int)) + (int)tab: %d\n", (7 * sizeof(int)) + (int)tab);
  printf("7 + tab: %d\n", 7 + tab);
}

The exact results will vary, but you'll see that &tab[7] is just some math done on tab to figure out what memory address to examine.

$ ./test
sizeof(int): 4
tab: 1595446448
&tab[7]: 1595446476
(7 * sizeof(int)) + (int)tab: 1595446476
7 + tab: 1595446476

1595446476 - 1595446448 is 28. 7 * 4 is 28.

like image 31
Schwern Avatar answered Dec 20 '22 22:12

Schwern