0

In the following code there is a memory violation potential when pos is negative value. Why there is a segmentation fault only when pos=-2; why the bus error occurs only when pos=-5,-6,-7 etc.? (Those results are consistent).

#include <stdio.h>


#define TABLE_SIZE (800)
int table[TABLE_SIZE] = {0};

    int insert_in_table(int val, int pos){
        int int_size = sizeof(int);
        if(pos > TABLE_SIZE / int_size){
            printf("pos is too big\n");
            return -1;
        }


        table[pos] = val;
        printf("table[%u]=%d\n", pos, table[pos]);

        return 0;
    }

    int main(void){

        int pos, val;
        printf("Position:\n");
        scanf("%d", &pos);
        printf("Value:\n");
        scanf("%d", &val);

        return insert_in_table(val, pos);
    }

This is a running example on my machine, Mac OSX:

My $./prog
Position:
-1
Value:
3
table[4294967295]=3
My $./prog
Position:
-2
Value:
3
Segmentation fault: 11
My $./prog
Position:
-3
Value:
5
table[4294967293]=5
My $./prog
Position:
-4
Value:
3
table[4294967292]=3
My $./prog
Position:
-5
Value:
3
Bus error: 10
My $./prog
Position:
-6
Value:
3
Bus error: 10
My $./prog
Position:
-7
Value:
3
Bus error: 10
Bush
  • 2,433
  • 5
  • 34
  • 57
  • You can't have negative array indexers, looking at `table[pos]`. They'll get interpreted at very large ints, which will be much larger than your allocated size, hence the error. – Chris O Oct 25 '15 at 22:26
  • Have you read my question? – Bush Oct 25 '15 at 22:27
  • Sorry just figured out what you are trying to ask. – Chris O Oct 25 '15 at 22:27
  • 1
    Meditate about the implications of **undefined** behaviour, provide a [mcve] and use the correct types! – too honest for this site Oct 25 '15 at 23:05
  • 2
    @Chris O Disagree with "You can't have negative array indexers" as a general assertion, http://stackoverflow.com/q/3473675/2410359 – chux - Reinstate Monica Oct 25 '15 at 23:19
  • 1
    @chux Fair enough, I should have clarified that using a negative index, to point at memory before that area you just allocated, is in fact UB, which is topic of this code. But the example you've provided shows a perfectly valid use case. – Chris O Oct 26 '15 at 03:07

2 Answers2

2

Don't matter. It's undefined behavior.

The C standard itsself doesn't specify any signals or termination causes for processes. It's the OS (or POSIX) that defines them. So, from a pure C perspective, the question itsself is flawed because undefined behavior is undefined behavior. Anything might happen.


We can go one level deeper, beneath the C standard and to the operating system. Wikipedia defines a bus error like this:

[...] a bus error is a fault raised by hardware, notifying an operating system (OS) that a process is trying to access memory that the CPU cannot physically address [...]

while a segmentation fault only denotes an invalid memory access (including lack of privileges to access certain memory areas) in general. So, apparently the bus error occured because you attempted to address a memory location the CPU isn't capable of addressing at all while the segmentation fault only displayed the access of an invalid memory location.

cadaniluk
  • 15,027
  • 2
  • 39
  • 67
  • This. If you're writing outside valid memory, there is no "weird behavior". Heh. I would guess that both the segfault and the bus error are not actually immediate effects of the table write, but rather indirect effects of the write corrupting some other memory location. If you're really interested, you may be able to figure it out through gdb. – Snild Dolkow Oct 25 '15 at 22:31
  • but how come I manage to assign to `table[-1]`? (which is `*(table + (4294967295 * sizeof(int))`) – Bush Oct 25 '15 at 22:35
  • @SnildDolkow can you explain more on how to get the actual reason using gdb? – Bush Oct 25 '15 at 22:36
  • @Bush I guess `typeof(size_t * int)` is not capable of holding such a big value, so the value overflows. I don't know the size of the data types on your machine but you should be able to calculate the result of the overflow. `table[-1]` then accesses some random memory location, yielding undefined behavior. – cadaniluk Oct 25 '15 at 22:37
  • @Bush Just compile with `-g` enabled and attach gdb. When the erring signal is raised you can check out the registers' values, memory locations etc. to find out the actual address you tried to access. – cadaniluk Oct 25 '15 at 22:40
  • 1
    @Bush: I can't reproduce on my computer (x86_64 Ubuntu 14.04), but I'll see if I can get some nice steps together anyway. I'll post it as a separate answer, so I can use some nice formatting. – Snild Dolkow Oct 25 '15 at 22:41
1

As requested, I did some gdb:ing.

tmp$ gcc -O2 -g -Wno-unused-result tmp.c -o bushmem
tmp$ gdb bushmem 
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from bushmem...done.
(gdb) break tmp.c:15
Breakpoint 1 at 0x40066c: file tmp.c, line 15.
(gdb) run
Starting program: /tmp/bushmem 
Position:
-2
Value:
3

Breakpoint 1, insert_in_table (val=3, pos=-2) at tmp.c:15
15          table[pos] = val;
(gdb) info symbol table
table in section .bss of /tmp/bushmem
(gdb) info symbol &table[2]
table + 8 in section .bss of /tmp/bushmem
(gdb) info symbol &table[-2]
No symbol matches &table[-2].
(gdb) info proc mappings
process 20372
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
            0x400000           0x401000     0x1000        0x0 /tmp/bushmem
            0x600000           0x601000     0x1000        0x0 /tmp/bushmem
            0x601000           0x602000     0x1000     0x1000 /tmp/bushmem
      0x7ffff7a15000     0x7ffff7bd0000   0x1bb000        0x0 /lib/x86_64-linux-gnu/libc-2.19.so
      0x7ffff7bd0000     0x7ffff7dcf000   0x1ff000   0x1bb000 /lib/x86_64-linux-gnu/libc-2.19.so
      0x7ffff7dcf000     0x7ffff7dd3000     0x4000   0x1ba000 /lib/x86_64-linux-gnu/libc-2.19.so
      0x7ffff7dd3000     0x7ffff7dd5000     0x2000   0x1be000 /lib/x86_64-linux-gnu/libc-2.19.so
      0x7ffff7dd5000     0x7ffff7dda000     0x5000        0x0 
      0x7ffff7dda000     0x7ffff7dfd000    0x23000        0x0 /lib/x86_64-linux-gnu/ld-2.19.so
      0x7ffff7fcd000     0x7ffff7fd0000     0x3000        0x0 
      0x7ffff7ff6000     0x7ffff7ffa000     0x4000        0x0 
      0x7ffff7ffa000     0x7ffff7ffc000     0x2000        0x0 [vdso]
      0x7ffff7ffc000     0x7ffff7ffd000     0x1000    0x22000 /lib/x86_64-linux-gnu/ld-2.19.so
      0x7ffff7ffd000     0x7ffff7ffe000     0x1000    0x23000 /lib/x86_64-linux-gnu/ld-2.19.so
      0x7ffff7ffe000     0x7ffff7fff000     0x1000        0x0 
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]
(gdb) p/x &table[0]
$2 = 0x601080
(gdb) info symbol 0x601080
table in section .bss of /tmp/bushmem
(gdb) info symbol 0x601084
table + 4 in section .bss of /tmp/bushmem
(gdb) x/32 &table[-16]
0x601040:                   0x00000000  0x00000000  0x00000000  0x00000000
0x601050:                   0x00000000  0x00000000  0x00000000  0x00000000
0x601060 <completed.6973>:  0x00000000  0x00000000  0x00000000  0x00000000
0x601070:                   0x00000000  0x00000000  0x00000000  0x00000000
0x601080 <table>:           0x00000000  0x00000000  0x00000000  0x00000000
0x601090 <table+16>:        0x00000000  0x00000000  0x00000000  0x00000000
0x6010a0 <table+32>:        0x00000000  0x00000000  0x00000000  0x00000000
0x6010b0 <table+48>:        0x00000000  0x00000000  0x00000000  0x00000000

You can use help <cmd> in gdb to learn more about each command.

Okay, so what I did, in order:

  • Set a breakpoint at the table-writing line. GDB will stop the program every time we reach this line.
  • When we got there, I demonstrate the use of info symbol <addr>.
  • In my case, there was no symbol match for &table[-2] -- but remember that I couldn't reproduce, so your case may be different.
  • info proc mappings shows the virtual memory map of the program.
  • p/x simple prints a value in hex -- here, we print the address of table[0], which turns out to be at 0x601080. Note that this is in the third memory area, which started at 0x601000.
  • And as you can see, info symbol 0x601080 tells us that this address belongs to the table symbol. And 4 bytes later is table+4, as expected.
  • Finally, we print some memory contents, starting a bit before table. In my case, these were all zeroes -- in your case, they may have different values (or they may be zero, but that value was important for something).

After this, you should be able to run the continue command, and gdb should catch where the crash happens for you. Use the bt command to see where it happened. But the location of the crash is probably not the same as the location where your modified memory was read (and hence where things really started to go wrong).

If you want, you could try the rwatch command in gdb. Setting it on &table[-2] should make gdb stop when someone reads that memory, which may help you figure out what code is responsible for that. And maybe what it was used for.

Community
  • 1
  • 1
Snild Dolkow
  • 6,669
  • 3
  • 20
  • 32
  • Yeah, debuggers are cool. I wish I'd realized that earlier in my programming learning process -- it's so much easier than just guessing at problems. :) – Snild Dolkow Oct 26 '15 at 00:02