【发布时间】:2016-09-17 20:09:08
【问题描述】:
我们有一个 C++ 库。我们正在提供自定义断言并放弃 Posix NDEBUG 和 assert(下面的背景故事)。
断言在 Windows 下看起来像这样:
# define CRYPTOPP_ASSERT(exp) { \
if (!(exp)) { \
std::ostringstream oss; \
oss << "Assertion failed: " << (char*)(__FILE__) << "(" \
<< (int)(__LINE__) << "): " << (char*)(__FUNCTION__) \
<< std::endl; \
std::cerr << oss.str(); \
DebugBreak(); \
} \
}
我们遇到的问题是,我们必须包含<windows.h>,这会带来很多额外的麻烦,即使定义了WIN32_LEAN_AND_MEAN。一些额外的麻烦,如min 和max,会破坏 C++ 编译。事实上,测试我们的更改让我们崩溃了。
我们通过<windows.h> 搜索“仅包含调试”类型定义,但找不到它。我们也试过根据Microsoft's docs on DebugBreak添加extern void WINAPI DebugBreak(void);,但是由于重新定义符号导致编译错误。
添加NO_MIN_MAX(我认为这是宏)不是一个选项,因为当定义从我们的标题交叉传到用户代码时,我们正在更改用户程序中的定义。相关,见Limiting Scope of #include Directives和朋友。
使用#pragma push_macro 和#pragma pop_macro 不是一个选项,因为我们支持Microsoft 编译器回到VC++ 6.0。最早的pragma可用的是VS2003。
由于中断,我们不想要包含<windows.h>。如何在不包含 Windows.h 的情况下获得 DebugBreak 的声明?
提前致谢。
这是一个简化的案例:
// cl.exe /c assert.cpp
#include <algorithm>
// #include <windows.h>
// #ifndef WINAPI
// # define WINAPI __stdcall
// #endif
// extern void WINAPI DebugBreak(void);
#define MY_ASSERT(exp) { \
if (!(exp)) { \
DebugBreak(); \
} \
}
void dummy(int x1, int x2)
{
MY_ASSERT(x1 == std::min(x1, x2));
}
这是使用我们的 extern 声明模拟用户程序时的错误。此测试发生在安装了 VS2012 和 VS2013 的 Windows 8.1 x64 上。我们还使用了开发人员命令提示符。
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
assert.cpp
assert.cpp(11) : warning C4273: 'DebugBreak' : inconsistent dll linkage
C:\Program Files (x86)\Windows Kits\8.1\include\um\debugapi.h(70) : see
previous definition of 'DebugBreak'
assert.cpp(21) : error C2589: '(' : illegal token on right side of '::'
assert.cpp(21) : error C2059: syntax error : '::'
assert.cpp(21) : error C2143: syntax error : missing ';' before '{'
当我们检查 <debugapi.h> 时,我们看到:
WINBASEAPI
VOID
WINAPI
DebugBreak(
VOID
);
WINBASEAPI 扩展为其他宏。我不认为我们将能够在所有平台上都得到它们。
我们有一个跨平台的 C++ 安全库,它最近捕获了CVE-2016-7420。如果断言被触发,可能会丢失数据,因此它被归类为信息泄露。当敏感数据导出到文件系统(核心转储和崩溃报告)时会发生丢失;并出口到第三方(Apple 通过 CrashReporter、Ubuntu 通过 Appor、Microsoft 通过 Windows 错误报告、开发人员等)。
断言从未在我们的生产/发布中触发,因为我们的 Makefile 和 Visual Studio 解决方案很好地配置了库。在生产/发布版本中,断言被删除,C++ throw() 处理错误情况。断言存在于调试/开发人员配置中,因此代码将自行调试,并将程序员从任务中解脱出来。
我们分析后发现documenting "release/production builds must use -DNDEBUG" is an incomplete remediation。人们不会阅读文档;如果 RTFM 能够正常工作,那么它现在已经发生了。此外,CMake 不会定义它,Autotools 不会定义它,Eclipse 不会定义它等等。我们实际上处于 CVE 之前的同一点。我们所做的只是在不降低风险的情况下转移责任。
【问题讨论】:
-
很抱歉,您有 minimal reproducible example 吗?如果你有,我会调查它。我在
<windows.h>定义 TRUE 和 FALSE 时遇到了这个问题,并在我只想让结构测量准确的时间并将其整理出来时破坏了一些东西。 -
代码调试步骤:1) 阅读,2) 记录,3) 逐步检查,4) 设置陷阱 5) 为客户设置陷阱(在发布版本中)。
-
@Jean-FrançoisFabre - 很公平。完成。
-
“一些多余的东西,比如
min和max,会破坏C++ 编译。” - 这就是#define NOMINMAX的原因。 Windows 头文件可以驯服。我不是建议你应该走这条路,但如果你必须这样做,这很有可能。 -
@IInspectable - 请发布您使用
NOMINMAX的代码,这样它就不会从我们的标题中交叉传播到用户标题中。我很乐意对其进行测试。
标签: c++ windows visual-studio assert