使用GDB无法找到堆栈溢出函数

5

我有以下的C应用程序:

#include <stdio.h>

void smash()
{
    int i;
    char buffer[16];
    for(i = 0; i < 17; i++)  // <-- exceeds the limit of the buffer
    {
        buffer[i] = i;
    }
}

int main()
{
    printf("Starting\n");
    smash();
    return 0;
}

我使用以下版本的gcc进行交叉编译:

armv5l-linux-gnueabi-gcc -v
Using built-in specs.
Target: armv5l-linux-gnueabi
Configured with: /home/tarjeif/svn/builder/build_armv5l-linux-gnueabi/gcc-4.4.1/gcc-4.4.1/configure --target=armv5l-linux-gnueabi --host=i486-linux-gnu --build=i486-linux-gnu --prefix=/home/tarjeif/svn/builder/build_armv5l-linux-gnueabi/toolchain --with-sysroot=/home/tarjeif/svn/builder/build_armv5l-linux-gnueabi/toolchain --with-headers=/home/tarjeif/svn/builder/build_armv5l-linux-gnueabi/toolchain/include --enable-languages=c,c++ --with-gmp=/home/tarjeif/svn/builder/build_armv5l-linux-gnueabi/gmp-5.0.0/gmp-host-install --with-mpfr=/home/tarjeif/svn/builder/build_armv5l-linux-gnueabi/mpfr-2.4.2/mpfr-host-install --disable-nls --disable-libgcj --disable-libmudflap --disable-libssp --disable-libgomp --enable-checking=release --with-system-zlib --with-arch=armv5t --with-gnu-as --with-gnu-ld --enable-shared --enable-symvers=gnu --enable-__cxa_atexit --disable-nls --without-fp --enable-threads
Thread model: posix
gcc version 4.4.1 (GCC) 

这样调用:

armv5l-linux-gnueabi-gcc -ggdb3 -fstack-protector-all -O0 test.c

在目标上运行时,它会输出:
Starting
*** stack smashing detected ***: ./a.out terminated
Aborted (core dumped)

我使用gdb加载生成的核心转储,得到以下回溯信息:

GNU gdb (GDB) 7.0.1
Copyright (C) 2009 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 "--host=i486-linux-gnu --target=armv5l-linux-gnueabi".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/andersn/workspace/stacktest/a.out...done.
Reading symbols from /home/andersn/workspace/stacktest/linux/toolchain/lib/libc.so.6...done.
Loaded symbols for /home/andersn/workspace/stacktest/linux/toolchain/lib/libc.so.6
Reading symbols from /home/andersn/workspace/stacktest/linux/toolchain/lib/ld-linux.so.3...done.
Loaded symbols for /home/andersn/workspace/stacktest/linux/toolchain/lib/ld-linux.so.3
Reading symbols from /home/andersn/workspace/stacktest/linux/toolchain    /lib/libgcc_s.so.1...done.
Loaded symbols for /home/andersn/workspace/stacktest/linux/toolchain/lib/libgcc_s.so.1
Core was generated by `./a.out'.
Program terminated with signal 6, Aborted.
#0  0x40052d4c in *__GI_raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:67
67  ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
    in ../nptl/sysdeps/unix/sysv/linux/raise.c
(gdb) bt
#0  0x40052d4c in *__GI_raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:67
#1  0x40054244 in *__GI_abort () at abort.c:92
#2  0x40054244 in *__GI_abort () at abort.c:92
#3  0x40054244 in *__GI_abort () at abort.c:92
#4  0x40054244 in *__GI_abort () at abort.c:92
#5  0x40054244 in *__GI_abort () at abort.c:92
#6  0x40054244 in *__GI_abort () at abort.c:92
... and so on ...

现在,问题来了: 我通过GDB完全找不到导致堆栈溢出的函数,尽管smash()函数没有覆盖堆栈的任何结构数据,仅覆盖了堆栈保护本身。我该怎么办?


1
我认为你的“等等”的可能省略了一些重要信息。__GI_abort真的是堆栈底部吗? - SoapBox
我尝试继续GDB输出并在达到#11087之前放弃了...所有帧都相等。 - anorm
你正在明确破坏堆栈 - 不要期望核心文件有整齐的链接调用帧 :) - Nikolai Fetissov
我认为栈应该是完全有效的,因为我只超出了合法范围1个字节,而那就是栈保护器的位置。 - anorm
@Nikolai N Fetissov:在Linux AMD64(2.6.35)上进行测试,按照以上步骤(循环次数为31且ulimit -c无限制),使用gcc(x86_64 4.4.5),会出现核心转储,并且gdb(7.2)可以正确解析堆栈帧。 - Eric Towers
3个回答

8
问题在于编译目标libc.so.6的GCC版本存在缺陷,未能为__GI_raise发出正确的展开描述符。没有正确的展开描述符,GDB在展开堆栈时会进入循环。
您可以使用以下命令来检查展开描述符:
readelf -wf /home/andersn/workspace/stacktest/linux/toolchain/lib/libc.so.6

我希望您能够理解,在任何调用abort的程序中,您都可以在GDB中获得完全相同的结果。

#include <stdlib.h>
void foo() { abort(); }
int main() { foo(); return 0; }

很遗憾,除了尝试构建新版本的GCC并使用它重新构建整个“世界”,您没有太多可做的。


是的,在简短概括一下,就是这样。不过,重新用新编译器构建libc.so可能已经足够了。 - Eugene Smith
仅重新构建libc.so.6可能不足够:毕竟,也无法保证foo()将使用“旧”GCC正确获取展开描述符。 - Employed Russian

2

即使使用了-fstack-protector-all(甚至使用-Wstack-protector警告未受保护的帧函数),GDB也不总是能够确定发生了什么导致堆栈崩溃。示例

在这些情况下,堆栈保护程序已经完成了它的工作(杀死了一个行为不端的应用程序),但并没有对调试器有任何好处。(经典的例子是堆栈破坏,其中写入具有足够大步幅以跳过canary。)在这些情况下,可能需要通过断点二分搜索代码,以缩小导致崩溃的代码区域,然后逐步跟踪崩溃以查看其发生方式。


0
你尝试过解决这个投诉吗:“../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.”看看是否能够解决符号呢?

没有,但是 GDB 在给定位置并没有丢失符号信息,只是缺少用于列表的源代码文件。不过,我会尝试您的建议...谢谢。 - anorm
好的,我尝试过了但没有成功。GDB显示了raise.c中的源代码行,但回溯仍然是损坏的。 - anorm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接