【问题标题】:Program Crashes in 64 bit System64 位系统中的程序崩溃
【发布时间】:2014-06-24 11:08:09
【问题描述】:

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

【问题讨论】:

  • 因为dp->d_name[len - 3] 在您的 32 位进程内(偶然)而不是在您的 64 位进程内。为什么你需要做这个非常危险的检查?
  • @llya 在您的 32 位进程内(偶然)而不是在您的 64 位进程内...请您详细说明此声明。
  • 只是想知道,您是否包含<stdlib.h>
  • 那么当dp->d_name 成立时,你认为阅读dp->d_name[len - 3] 对你有什么好处,比如"."。嗯.....它的UB。你的问题似乎是在询问关于 不是 的东西 确定性 的东西。
  • @user61455 你明白你的代码被破坏了吗?修复它不是最简单的。它一直被打破。它到处都坏了。到目前为止,您真的很不走运,从未见过段错误。现在你很幸运,发现了错误。

标签: c++ c gdb


【解决方案1】:

您的程序会导致未定义的行为,正如您似乎意识到的那样。您正在尝试访问数组边界之外的内容。未定义的行为就是它听起来的样子。行为未定义。什么事情都可能发生。

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 你应该提到,因为UB,代码到处都被破坏了
    • 在这两种情况下肯定是UB。但据了解,问题是为什么第一个 UB 不会崩溃,而第二个 UB 会导致崩溃。
    • 我不确定提问者是否真的理解程序总是坏的。我认为asker认为它在32位下没问题。
    • 是的,我知道程序总是坏掉。我的疑问是为什么第一个 UB 不会崩溃,而第二个 UB 会导致崩溃。
    【解决方案3】:

    dp-&gt;d_name[len - 3] == 'j'len - 3 可能在这台 32 位机器上的段内,而在 64 位机器上的段外。这与您的操作系统有关。

    【讨论】:

      猜你喜欢
      • 2011-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-30
      • 2011-07-07
      • 1970-01-01
      相关资源
      最近更新 更多