以下代码在64位系统中崩溃。如果文件名长度小于3,
然后'len'
发生了下溢。但是这个节目没有任何表现
32位系统中的分段故障。但我在64岁时遇到了分段错误
位系统。为什么这个程序没有显示32位的任何分段错误
系统?
DIR * dirp = opendir(dirPath);
struct dirent * dp;
while(dirp)
{
if((dp = readdir(dirp)) != NULL)
{
unsigned int len = strlen(dp->d_name);
//underflow happens if filename length less than 3
if((dp->d_name[len - 3] == 'j'))
}
}
答案 0 :(得分:5)
您可能会看到未定义的行为。您试图访问数组边界之外。并且未定义的行为正是听起来的样子。行为未定义。什么事情都可能发生。
一旦运行,您可能会遇到分段错误,而不是另一次。或者您可能会在不同的编译器下看到不同的行为。未定义的行为本质上是不可预测的。你似乎在一个编译器下的代码中逃脱了这个错误的事实并没有使你的代码正确。
显然,你应该做的是避免编写导致未定义行为的程序。
答案 1 :(得分:4)
为什么这个程序在32位系统中没有显示任何分段错误?
看,这稍微简化了你的程序:
1 int main(int argc, char *argv[])
2 {
3 char name[100];
4 unsigned int len = 3;
5 name[len-argc] = 1;
6 return 0;
7 }
因此,当我将其构建为32位程序gcc -m32 -g main.c -o main32
时,这就是gdb下进程的地址空间的样子:
$ gdb -q --args ./main32 1 2 3
Reading symbols from /home/main32...done.
(gdb) start
(gdb) info proc mappings
process 28330
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x110000 0x111000 0x1000 0x0 [vdso]
0x3fa000 0x418000 0x1e000 0x0 /lib/ld-2.12.so
0x418000 0x419000 0x1000 0x1d000 /lib/ld-2.12.so
0x419000 0x41a000 0x1000 0x1e000 /lib/ld-2.12.so
0x41c000 0x5a8000 0x18c000 0x0 /lib/libc-2.12.so
0x5a8000 0x5aa000 0x2000 0x18c000 /lib/libc-2.12.so
0x5aa000 0x5ab000 0x1000 0x18e000 /lib/libc-2.12.so
0x5ab000 0x5ae000 0x3000 0x0
0x8048000 0x8049000 0x1000 0x0 /home/main32
0x8049000 0x804a000 0x1000 0x0 /home/main32
0xf7fdf000 0xf7fe0000 0x1000 0x0
0xf7ffd000 0xf7ffe000 0x1000 0x0
0xfffe9000 0xffffe000 0x15000 0x0 [stack]
(gdb) p/x &(name[len-argc])
$2 = 0xffffcfab
正如您所见name[3-4]
(正如您所说的underflow
)实际上指向堆栈上的有效地址。这就是您的流程不会崩溃的原因。
当我构建与64位(gcc -m64 -g main.c -o main64
)相同的程序时,该地址无效
(gdb) info proc mappings
process 29253
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x400000 0x401000 0x1000 0x0 /home/main64
0x600000 0x601000 0x1000 0x0 /home/main64
0x3c40a00000 0x3c40a20000 0x20000 0x0 /lib64/ld-2.12.so
0x3c40c1f000 0x3c40c20000 0x1000 0x1f000 /lib64/ld-2.12.so
0x3c40c20000 0x3c40c21000 0x1000 0x20000 /lib64/ld-2.12.so
0x3c40c21000 0x3c40c22000 0x1000 0x0
0x3c41200000 0x3c41389000 0x189000 0x0 /lib64/libc-2.12.so
0x3c41389000 0x3c41588000 0x1ff000 0x189000 /lib64/libc-2.12.so
0x3c41588000 0x3c4158c000 0x4000 0x188000 /lib64/libc-2.12.so
0x3c4158c000 0x3c4158d000 0x1000 0x18c000 /lib64/libc-2.12.so
0x3c4158d000 0x3c41592000 0x5000 0x0
0x7ffff7fdd000 0x7ffff7fe0000 0x3000 0x0
0x7ffff7ffd000 0x7ffff7ffe000 0x1000 0x0
0x7ffff7ffe000 0x7ffff7fff000 0x1000 0x0 [vdso]
0x7ffffffea000 0x7ffffffff000 0x15000 0x0 [stack]
0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall]
(gdb) p/x &name[len-argc]
$5 = 0x8000ffffde3f
还有一件事。这就是汇编程序查找64位应用程序的方式:
(gdb) disassemble /m
Dump of assembler code for function main:
5 name[len-argc] = 1;
0x0000000000400472 <+22>: mov -0x74(%rbp),%edx
0x0000000000400475 <+25>: mov -0x4(%rbp),%eax
0x0000000000400478 <+28>: sub %edx,%eax
0x000000000040047a <+30>: mov %eax,%eax
=> 0x000000000040047c <+32>: movb $0x1,-0x70(%rbp,%rax,1)
这是$ eax ::
(gdb) p $eax
$1 = -1
但是,因为您处于64模式,所以指定使用rax
。这是$ rax:
(gdb) p/x $rax
$3 = 0xffffffff
因此,程序会向有效堆栈添加一个巨大的正偏移量,并导致无效的地址。
我想强调这是32和64模式下的未定义行为。如果您想修复此未定义的行为,您可以阅读我的另一个答案https://stackoverflow.com/a/24287919/184968。
答案 2 :(得分:1)
dp->d_name[len - 3] == 'j'
len - 3
可能位于此32位计算机上的细分市场中,位于64位计算机上的细分市场之外。它与您的操作系统有关。