如何调试“检测到堆栈溢出”错误?

28

我有一段复杂的C++代码。它是一个FastCGI程序,使用FastCGI C++类库

当我请求一个非常长的URL时,会出现以下情况:

*** stack smashing detected ***: ./tileserve terminated
Erreur de segmentation

对于实际应用,这不是问题,因为我从来没有使用过如此长的URL,但这意味着任何人都可以终止我的服务器...我不喜欢那样。

有没有工具可以找出这个问题出现在哪里?我该如何使用它?

编辑:已解决

我正在做这件事:

int len;
char uri[200];

len = strlen(request.params[std::string("REQUEST_URI")].c_str());
printf("%d\n", len);

if (len > 200) return 1;

strcpy(uri, request.params[std::string("REQUEST_URI")].c_str());

看起来 200 对于 len 测试来说太高了。实际上它在 194 处失败。

所以我改成了这样:

if (len > 190) return 1;

现在,没问题。

2
堆栈跟踪可以给你一些提示。 - Mahesh
11
+1 表示“错误的分段”(Erreur de segmentation) - trojanfoe
1
让我猜猜看。错误是由于URL太长,超出了您放置的缓冲区而引起的。 - Bart
2
检查URL进入的缓冲区...检查valgrind以获取溢出的确切位置。 - matthias krull
2
如果编写类似于Web服务器的程序,请不要使用strcpy这样的函数,因为这可能存在安全问题。相反,应该使用像strncpy这样的函数,以确保您不会将太多内容复制到缓冲区中。 - Joakim
Len = 200 默认值太高了。一个长度为200的字符数组只能容纳长度为199的字符串。 - Swift - Friday Pie
2个回答

25

如果您阅读该网站,您会意识到这只是一个简单的C++封装,覆盖了一个C库。

C库通常存在缓冲区溢出的问题:

#include <cstring>
#include <cstdio>

int main(int argc, char* argv[]) {
  char buffer[16]; // ought to be sufficient

  strcpy(buffer, argv[1]);
  printf("%s", buffer);
}

尝试运行这个程序:

> ./test "a"
a
> ./test "abcdefghijklmnoprqstuvwxyz"
???

由于缓冲区只能容纳16个字符,剩余字符将被写到其末尾。这就是堆栈破坏,是未定义行为

在某些情况下,运行时库或操作系统的某些实现可能会检测到此情况并终止程序。

要么是做错了什么,要么是的问题。

为了定位问题,您可以使用Valgrind或在调试器中运行程序。或者,如果您的系统允许,您可能会在程序被杀死的那一刻拥有内存转储。您还可以在调试器中查看此内存转储。


2
是的,我明白问题出在哪里了。问题在于定位它... - user1219721
@user1219721:抱歉,我错过了那部分。使用调试器(VC++中集成的或Linux上的gdb)。 - Matthieu M.
Valgrind在这里无用,因为它不能检测基于堆栈的内存错误 - 只能检测堆内存损坏。 - Charles Salvia
@CharlesSalvia:在我提供的玩具示例中是这样的。一般来说...这有点困难:我怀疑是缓冲区溢出,但也可能是读取未初始化的值并将其用作偏移量。 - Matthieu M.
@MatthieuM。+1并删除了我的评论。 - qdii
@CharlesSalvia Gcc有一个堆栈保护选项,可以查看正确的回溯信息。要找到smasher是谁,应该找出写入坏数据的地址,并在该地址上设置断点监视。 - Swift - Friday Pie

2
你可以使用类似于valgrind的工具,或者你的编译器可能有静态分析功能,可以找到可能超出缓冲区的地方。
此外,您可以仅审核代码以查找使用易错函数(如strcpy)的情况,并将它们替换为安全函数(如strncpy),或者更好的方法是使用管理自己内存的对象,例如std :: string。

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