如何在Windows上报告堆栈缓冲区溢出?

时间:2016-05-25 05:20:01

标签: c++ exception

在下面显示的代码中,我使用了所有记录的方法来检测异常并生成诊断。它使用C ++ try / catch关键字,使用__try/__catch扩展关键字捕获SEH异常,使用Windows的AddVectoredExceptionHandler()和SetUnhandledExceptionFilter()winapi函数来安装VEH / SEH过滤器。

使用Visual C ++ 2003运行:
    / GS:输出“你好,世界!”并以退出代码0终止。
    / GS-:输出“你好,世界!”并以退出代码0终止。

使用Visual C ++ 2013运行它:
    / GS:无输出,以退出代码-1073740791终止     / GS-:输出“你好,世界!”并以0退出终止。

如何在/ GS生效的VS2013编译程序中生成诊断?

#include "stdafx.h"
#include <Windows.h>

#define CALL_FIRST 1  
#define CALL_LAST 0

LONG WINAPI MyVectoredHandler(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
    UNREFERENCED_PARAMETER(ExceptionInfo);

    printf("MyVectoredHandler\n");
    return EXCEPTION_CONTINUE_SEARCH;
}

LONG WINAPI MyUnhandledExceptionFilter(_In_ struct _EXCEPTION_POINTERS *ExceptionInfo)
{
    printf("SetUnhandledExceptionFilter\n");

    return EXCEPTION_CONTINUE_SEARCH;
}

void f()
{
    __try
    {
        char p[20] = "hello,world!";
        p[24] = '!';
        printf("%s\n", p);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        printf("f() exception\n");
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    AddVectoredExceptionHandler(CALL_FIRST, MyVectoredHandler);
    SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);

    try{
        f();
    }
    catch (...){
        printf("catched f exception\n");
    }
    return 0;
}

3 个答案:

答案 0 :(得分:4)

处理堆栈缓冲区溢出检测的CRT函数__report_gsfailure()假定堆栈帧损坏是由恶意软件攻击引起的。此类恶意软件传统上与fs:[0] SEH异常过滤器(存储在堆栈帧上)混淆,以获取异常处理程序以触发恶意软件负载。将数据转换为可执行代码的方法之一。

因此,CRT函数不能假设抛出异常是安全的。在VS2013中包含的CRT中不再存在,可以追溯到~VS2005。如果操作系统支持它将失败,如果不支持,则确保注册的VEH / SEH异常处理程序也看不到异常。除非你连接了调试器,否则Kaboom会崩溃到桌面而不进行诊断。

请与您的主管讨论此事,鉴于对您的客户承担巨大责任,您绝不想对此负个人责任。历史很少讲述一个程序员发生了什么,他的代码使整个公司停业一个月。但肯定没什么好看的。

将此代码粘贴到main()函数附近:

__declspec(noreturn) extern "C"
void __cdecl __report_gsfailure() {
    RaiseException(STATUS_STACK_BUFFER_OVERRUN, EXCEPTION_NONCONTINUABLE, 0, nullptr);
}

并计划很快将其删除。

答案 1 :(得分:3)

问题没有解决方案。

超出数组会导致标准C ++中出现未定义的行为,因此无法保证特定的结果。未能提供可靠的结果不是编译器的问题 - 它是允许的行为。

我知道没有任何实现可以保证响应溢出的任何特定行为 - VS当然不会。这并不奇怪,因为编译器不需要这样做(实质上,就是未定义行为的含义)。原因在于,通常很难可靠或一致地检测到这种情况。

这意味着检测数组溢出的唯一一致方法是在使用它们访问数组元素并采取适当的操作之前检查数组索引是否有效(例如,抛出可以捕获的异常而不是执行错误的操作) 。缺点是它没有提供一种简单或可靠的方法来捕获任意代码中的错误 - 除了修改所有代码以进行必要的检查。

答案 2 :(得分:1)

我本想对已接受的答案发表评论,但我刚刚加入,还没有足够的声誉来做到这一点。

我在Visual Studio 2017中尝试了该解决方案,并且不得不进行一些更改才能使该解决方案得以编译。

首先,我必须更改__report_gsfailure的签名以匹配Microsoft的头文件之一,以修复编译错误。

function nameRollCall(names) {
  var names = ['Joe', 'Bob', 'Mike'];
  let joinedNames = ''
  for (let i = 0; i < names.length; i++) {
    joinedNames += `${i+1}. ${names[i]} `
  }
  console.log(joinedNames)
}

nameRollCall()

接下来,我遇到一个LNK2005错误,可以通过将/ FORCE:MULTIPLE添加到项目属性的链接器->命令行中来纠正该错误。