【问题标题】:VERY strange stack overflow in C++ programC++程序中非常奇怪的堆栈溢出
【发布时间】:2011-03-03 12:45:51
【问题描述】:

我前段时间写了一个程序(Mac OS X、C++、SDL、FMOD),它的性能相当不错。但最近我想扩展它的功能并添加更多代码。现在,当我运行它并尝试测试新功能时,程序会因 SIGABRT 而崩溃。

查看调试器,在函数堆栈上我看到:

  • _kill
  • 杀死$UNIX2003
  • 提高
  • __中止
  • __stack_chk_fail
  • odtworz

据我所知,“__stack_chk_fail”表示堆栈溢出。但这还不是最奇怪的。在这个函数“odworz”中,我有一些这样的代码:

...

koniec = 0;
while ( koniec == 0 ) {
    ...
    if (mode == 1) {
        ...
    }
    else if (mode == 2) {
        ...
    }
    else if (mode == 3) {
       piesniOrkiestrowe[0] = '\0'; 
       while ( piesniOrkiestrowe[0] == '\0' ) { 
           losowaPiesn(); 
           char * piesnOrkiestrowa = szukajPiesniOrkiestrowej(); 
           if ( piesnOrkiestrowa != NULL ) 
              strcpy(piesniOrkiestrowe, piesnOrkiestrowa); 
       } 
       char nowyPiesnPlik[25]; 
       sprintf(nowyPiesnPlik, "%sorch/%s", PIESNI_DIR.c_str(), piesniOrkiestrowe);
    }
}

mode 是一个全局变量,在之前的函数中设置为值“2”。现在想象一下——如果我删除了在这种模式下永远不会执行的第三个 if 语句(模式 == 3),程序不会崩溃!删除甚至无法执行的代码会有所帮助!

现在,我不想删除此代码,因为它用于我程序的其他模式。它在那里工作得很好。那么我可以在哪里搜索的任何提示?这可能有什么问题?

【问题讨论】:

  • 听起来像是一个陈旧的构建。你重建了所有使用这个函数的东西吗?

标签: c++ macos stack-overflow callstack


【解决方案1】:

不是堆栈溢出错误。检测到堆栈帧损坏时调用 __stack_chk_fail。破坏堆栈的传统方法是缓冲区溢出。导致它的代码不在您的 sn-p 中,而是在点中。


使用评论中的代码更新问题后:strcpy 和 sprintf 调用都是堆栈损坏的绝佳候选者。我在原始答案中提到的缓冲区溢出问题。猜测一下:nowyPiesnPlik 看起来非常小。 sprintf() 函数会将太多字符写入缓冲区并覆盖“canary”。当金丝雀被踩到时,运行时会发出哨声:)

您可以使数组更大。不是真正的解决方案,请为这些函数使用安全的替代方法,例如 snprintf()。我将避免提及 strncpy()。

【讨论】:

  • 你知道为什么删除mode == 3 部分对他有帮助吗?
  • 它没有,它只是隐藏了正在发生缓冲区溢出的事实。
  • mode == 3 中...中是否有局部变量?如果是这样,删除该分支可能会影响堆栈布局。
  • 我擦除的模式 == 3 中的代码如下所示:piesniOrkiestrowe[0] = '\0';而 ( piesniOrkiestrowe[0] == '\0' ) { losowaPiesn(); char * piesnOrkiestrowa = szukajPiesniOrkiestrowej(); if ( piesnOrkiestrowa != NULL ) strcpy(piesnOrkiestrowe, piesnOrkiestrowa); } 字符 nowyPiesnPlik[25]; sprintf(nowyPiesnPlik, "%sorch/%s", PIESNI_DIR.c_str(), piesniOrkiestrowe);
  • @mav:可能在mode == 3 中声明了一些变量等,因此如果包含此代码,堆栈帧的布局最终会有所不同。然后堆栈的其他部分碰巧损坏了,而不是堆栈检查函数验证的部分。
【解决方案2】:

一点也不奇怪。当谈到堆栈溢出或堆损坏时,您应该会感到奇怪。堆栈指针、程序计数器或其他程序状态已损坏,因此调试器或跟踪工具无法准确报告程序在崩溃时所在的位置。该错误可能在您的代码中的其他地方,远离您发布的 sn-p。从最近修改的代码开始。

编辑:您已经自己编写了一个很好的堆栈损坏示例,正如您后来发现的那样。无论如何,这是一个:

void foo (){ 
    int x[0];
    x[-99] = -1;
}

【讨论】:

  • 你能给我一些可能导致堆栈损坏的代码示例吗?我的程序大量使用了 cstrings 和 Strings。
【解决方案3】:

找到了!

罪魁祸首是在我给出的代码之前,但 Hans Passant 给了我一个线索,让我看看应该看什么。它看起来像这样:

char piesnPlik[25];
if ( mode == TRYB_PIANINO )
    sprintf(piesnPlik, "%spiano/%s.mp3", PIESNI_DIR.c_str(), wybranaPiesn);
else if ( tryb == TRYB_ORKIESTRA )
    sprintf(piesnPlik, "%sorch/%s", PIESNI_DIR.c_str(), piesniOrkiestrowe);
else if ( tryb == TRYB_NAGRANIE )
    sprintf(piesnPlik, "%s/%s", NAGRANIA_DIR.c_str(), nazwaNagraniaMP3);

所以,我今天添加了第三个 if which user "piesnPlik" 变量。但是“nazwaNagraniaMP3”比复制到那里的另外两个变量要长,所以它破坏了堆栈。但令人难以置信的是,它在之后设法与所有 SDL 东西一起工作,只是在从函数返回后崩溃。

感谢大家的建议!

【讨论】:

    【解决方案4】:

    我遇到了一个非常相似的问题,__stack_chk_fail 上的代码崩溃。 就我而言,上面推荐的解决方案是摆脱sprintf()

    【讨论】:

      猜你喜欢
      • 2012-02-14
      • 2013-09-04
      • 1970-01-01
      • 1970-01-01
      • 2020-12-03
      • 1970-01-01
      • 2013-12-12
      • 2010-11-05
      相关资源
      最近更新 更多