简体   繁体   English

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

[英]How to report a stack buffer overrun on Windows?

In the code shown below I have used all documented ways to detect an exception and produce a diagnostic. 在下面显示的代码中,我使用了所有记录的方法来检测异常并生成诊断。 It uses the C++ try/catch keywords, catches an SEH exception with the __try/__catch extension keywords, uses the Windows' AddVectoredExceptionHandler() and SetUnhandledExceptionFilter() winapi functions to install VEH/SEH filters. 它使用C ++ try / catch关键字,使用__try/__catch扩展关键字__try/__catch SEH异常,使用Windows的AddVectoredExceptionHandler()和SetUnhandledExceptionFilter()winapi函数来安装VEH / SEH过滤器。

Running this with Visual C++ 2003: 使用Visual C ++ 2003运行它:
/GS: outputs "hello,world!" / GS:输出“你好,世界!” and terminates with exit code 0. 并以退出代码0终止。
/GS-: outputs "hello,world!" / GS-:输出“你好,世界!” and terminates with exit code 0. 并以退出代码0终止。

Running this with Visual C++ 2013: 使用Visual C ++ 2013运行它:
/GS: no output, terminates with exit code -1073740791 / GS:无输出,以退出代码-1073740791终止
/GS-: outputs "hello,world!" / GS-:输出“你好,世界!” and terminates with exit with 0. 并以0退出终止。

How do I produce a diagnostic in a VS2013 compiled program with /GS in effect? 如何在/ 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;
}

The CRT function that handles stack buffer overruns detection, __report_gsfailure() , assumes that the stack frame corruption was induced by a malware attack. 处理堆栈缓冲区溢出检测的CRT函数__report_gsfailure()假定堆栈帧损坏是由恶意软件攻击引起的。 Such malware traditionally messed with the fs:[0] SEH exception filters (stored on the stack frame) to get an exception handler to trigger the malware payload. 此类恶意软件传统上与fs:[0] SEH异常过滤器(存储在堆栈帧上)混淆,以获取异常处理程序以触发恶意软件负载。 One of the ways to get data to turn into executable code. 将数据转换为可执行代码的方法之一。

So that CRT function cannot assume that throwing an exception is safe to do. 因此,CRT函数不能假设抛出异常是安全的。 And no longer does in the CRT included with VS2013, goes back to ~VS2005. 在VS2013中包含的CRT中不再存在,可以追溯到~VS2005。 It will failfast if the OS supports it and, if not, ensures that a registered VEH/SEH exception handler cannot see the exception either. 如果操作系统支持它将失败,如果不支持,则确保注册的VEH / SEH异常处理程序也看不到异常。 Kaboom, crash to the desktop with no diagnostic unless you have a debugger attached. 除非你附加了调试器,否则Kaboom会崩溃到桌面而不进行诊断。

The /SAFESEH option defeats this kind of malware attack so it isn't as serious as it once was. / SAFESEH选项可以抵御这种恶意软件攻击,因此它不像以前那么严重。 If you are still at the stage where your code suffers from stack corruption bugs and your app is not popular enough to become the target of malware then replacing the CRT function is something you could consider. 如果您仍处于代码遭受堆栈损坏错误的阶段,并且您的应用程序不足以成为恶意软件的目标,那么您可以考虑更换CRT功能。

Do talk this over with your supervisor, you never want to be personally responsible for this given the enormous liability to your client. 请与您的主管讨论这个问题,鉴于对您的客户承担巨大责任,您绝不希望对此负责。 History rarely tells the tale of what happened to the one programmer whose code put an entire corporation out of business for a month. 历史很少讲述一个程序员发生了什么,他的代码使整个公司停业一个月。 But surely wasn't anything pretty. 但肯定没什么好看的。

Paste this code somewhere close to your main() function: 将此代码粘贴到main()函数附近的某处:

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

And plan to remove it again soon. 并计划很快再次删除它。

There is no solution for the question as asked. 问题没有解决方案。

Overrunning an array causes undefined behaviour in standard C++, so no particular result is guaranteed. 超出数组会导致标准C ++中出现未定义的行为,因此无法保证特定结果。 Failure to give a reliable result is not a problem with the compiler - it is permitted behaviour. 未能提供可靠的结果不是编译器的问题 - 它是允许的行为。

I'm aware of no implementation that guarantees any specific behaviour in response to an overrun - VS certainly doesn't. 我知道没有任何实现可以保证响应溢出的任何特定行为 - VS当然不会。 Which is hardly surprising as compilers are not required to do that (that is, essentially, the meaning of undefined behaviour). 这并不奇怪,因为编译器不需要这样做(实质上,就是未定义行为的含义)。 The reason that is the case is that it is often difficult to reliably or consistently detect such occurrences. 这种情况的原因是通常难以可靠地或一致地检测这种情况。

This means the only consistent way to detect an array overrun is to check that array indices are valid BEFORE using them to access an array element and take appropriate actions (eg throw an exception which can be caught instead of doing the bad operation). 这意味着检测数组溢出的唯一一致方法是检查数组索引是否有效,然后使用它们来访问数组元素并采取适当的操作(例如,抛出可以捕获的异常而不是执行错误的操作)。 The downside is that it does not provide a simple or reliable way to catch errors in arbitrary code - short of modifying all code to do the required checks. 缺点是它没有提供一种简单或可靠的方法来捕获任意代码中的错误 - 除了修改所有代码以执行所需的检查。

I would have liked to comment on the accepted answer, but I just joined and don't have enough reputation to do that yet. 我本来想对已接受的答案发表评论,但我刚刚加入,并没有足够的声誉来做到这一点。

I tried the solution with Visual Studio 2017 and had to make a couple of changes to get the solution to compile. 我尝试使用Visual Studio 2017解决方案,并且必须进行一些更改才能获得编译解决方案。

First I had to change the signature of __report_gsfailure to match one of Microsoft's header files to fix a compilation error. 首先,我必须更改__report_gsfailure的签名以匹配Microsoft的一个头文件以修复编译错误。

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

Next I encountered a LNK2005 error, which I was able to correct by adding /FORCE:MULTIPLE to the Linker->Command Line for my project's properties. 接下来我遇到了一个LNK2005错误,我可以通过将/ FORCE:MULTIPLE添加到链接器 - >命令行来解决我的项目属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM