64位系统中的程序崩溃

Program Crashes in 64 bit System

本文关键字:程序 崩溃 系统 64位      更新时间:2023-10-16

以下代码在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'))
    }
  }

您的程序导致未定义的行为,正如您似乎意识到的那样。您正在尝试访问数组的边界之外。未定义行为就像它听起来的那样。行为没有定义。任何事情都有可能发生。

你可能会得到一个分割错误一次你运行,而不是另一次。或者您可能在不同的编译器下看到不同的行为。未定义行为本质上是不可预测的。事实上,您似乎在一个编译器下摆脱了代码中的这个错误,但这并不能使您的代码正确。

显然,你应该做的是避免编写导致未定义行为的程序。

为什么这个程序在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

但分配使用rax,因为你是在64模式。这是$rax:

的值
(gdb) p/x $rax
$3 = 0xffffffff

所以程序在一个有效的堆栈地址上增加了一个巨大的正偏移量,结果是无效的地址。

我想强调一下这在32和64模式下都是未定义的行为。如果你想修复这个未定义的行为,你可以阅读我的另一个答案https://stackoverflow.com/a/24287919/184968.

dp->d_name[len - 3] == 'j' len - 3可能在32位机器的段内,而在64位机器的段外。这和你的操作系统有关。