在以下代码中,pos
为负值时存在内存违规可能性。
为什么仅在pos=-2
时出现分段错误;为什么仅在pos=-5,-6,-7
等时发生总线错误? (这些结果是一致的)。
#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);
}
这是我的机器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
答案 0 :(得分:2)
无所谓。这是未定义的行为。
C标准本身没有指定进程的任何信号或终止原因。它是定义它们的OS(或POSIX)。因此,从纯粹的C角度来看,问题本身就存在缺陷,因为未定义的行为是未定义的行为。任何事都可能发生。
我们可以更深入一级,低于C标准和操作系统。 Wikipedia定义了这样的总线错误:
[...]总线错误是硬件引发的错误,通知操作系统(OS)进程正在尝试访问CPU无法物理寻址的内存[...]
虽然分段错误通常仅表示无效的内存访问(包括缺少访问某些内存区域的权限)。因此,显然发生了总线错误,因为您尝试寻址CPU根本无法寻址的内存位置,而分段错误仅显示无效内存位置的访问权。
答案 1 :(得分:1)
根据要求,我做了一些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
您可以在gdb中使用help <cmd>
来了解有关每个命令的更多信息。
好的,所以我做了,按顺序:
info symbol <addr>
。info proc mappings
显示程序的虚拟内存映射。p/x
simple以十六进制打印一个值 - 在这里,我们打印table[0]
的地址,结果为0x601080
。请注意,这是在第三个内存区域,从0x601000
开始。info symbol 0x601080
告诉我们此地址属于table
符号。正如预期的那样,后面的4个字节为table+4
。table
之前开始。就我而言,这些都是零 - 在您的情况下,它们可能具有不同的值(或者它们可能为零,但该值对于某些内容而言重要)。在此之后,您应该能够运行continue
命令,并且gdb应该捕获崩溃发生的位置。使用bt
命令查看它发生的位置。但崩溃的位置可能与您修改内存的读取位置不同(因此事情真的开始出错)。
如果您愿意,可以尝试the rwatch
command in gdb。在&table[-2]
上设置它应该会在有人读取内存时使gdb停止,这可能会帮助您找出导致该代码的代码。也许它用于什么。