使用 Conan 的编译器消毒器

为了更好地说明 Conan 与消毒器的集成,本节提供使用地址消毒器 (ASan) 和未定义行为消毒器 (UBSan) 的实际示例,使用简单的 C++ 程序。

作为第一步,请克隆源代码以重新创建此项目。您可以在 GitHub 上的 examples2 仓库 中找到它们

git clone https://github.com/conan-io/examples2.git
cd examples2/examples/security/sanitizers/compiler_sanitizers

在本示例中,我们将看到如何准备 Conan 以不同的方式使用消毒器。

为了展示如何在构建中使用消毒器,让我们考虑两个示例。

地址消毒器:越界索引

在本示例中,我们将构建一个简单的 C++ 程序,该程序有意访问数组中的越界索引,这应该在运行程序时触发 ASan。我们将使用 Conan profile 来启用 ASan

profiles/gcc_asan
 [settings]
 arch=x86_64
 os=Linux
 build_type=Debug
 compiler=gcc
 compiler.cppstd=gnu20
 compiler.libcxx=libstdc++11
 compiler.version=15
 compiler.sanitizer=Address

 [conf]
 tools.build:cflags=['-fsanitize=address']
 tools.build:cxxflags=['-fsanitize=address']
 tools.build:exelinkflags=['-fsanitize=address']
 tools.build:sharedlinkflags+=["-fsanitize=address"]

 [runenv]
 ASAN_OPTIONS=halt_on_error=1:detect_leaks=1

请注意,在此 profile 中,我们设置了 compiler.sanitizer=Address 并未定义使用哪些编译器标志,但这是一个设置,用于明确 ASan 和 UBSan 打算一起使用。

为了进一步说明,我们还使用环境变量 ASAN_OPTIONS=halt_on_error=1:detect_leaks=1 进行运行时配置,以管理 ASan 在第一个错误时停止执行,并在程序退出时检测内存泄漏。

index_out_of_bounds/main.cpp
#include <iostream>
#include <cstdlib>

int main() {
#ifdef __SANITIZE_ADDRESS__
  std::cout << "Address sanitizer enabled\n";
#else
  std::cout << "Address sanitizer not enabled\n";
#endif

  int foo[100];
  foo[100] = 42; // Out-of-bounds write

  return EXIT_SUCCESS;
}

注意: 上面的预处理器检查可移植于 GCC、Clang 和 MSVC。当 ASan 处于活动状态时,存在 __SANITIZE_ADDRESS__ 定义;

要使用 Conan 构建并运行此示例

cd index_out_of_bounds/
conan build . -pr ../profiles/gcc_asan
build/Debug/index_out_of_bounds

预期输出(缩写)

Address sanitizer enabled
==32018==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffbe04a6d0 ...
WRITE of size 4 at 0x7fffbe04a6d0 thread T0
#0 ... in main .../index_out_of_bounds+0x12ea
...
SUMMARY: AddressSanitizer: stack-buffer-overflow ... in main
This frame has 1 object(s):
[48, 448) 'foo' (line 11) <== Memory access at offset 448 overflows this variable

未定义行为消毒器:有符号整数溢出

本示例演示如何使用 UBSan 检测有符号整数溢出。它结合了 ASan 和 UBSan。创建一个专用的 profile

profiles/gcc_asan_ubsan
[settings]
arch=x86_64
os=Linux
build_type=Debug
compiler=gcc
compiler.cppstd=gnu20
compiler.libcxx=libstdc++11
compiler.version=15
compiler.sanitizer=AddressUndefinedBehavior

[conf]
tools.build:cflags+=["-fsanitize=address,undefined", "-fno-omit-frame-pointer"]
tools.build:cxxflags+=["-fsanitize=address,undefined", "-fno-omit-frame-pointer"]
tools.build:exelinkflags+=["-fsanitize=address,undefined"]
tools.build:sharedlinkflags+=["-fsanitize=address,undefined"]

它受 GCC 和 Clang 支持。MSVC 不支持 UBSan。

源代码

signed_integer_overflow/main.cpp
#include <iostream>
#include <cstdlib>
#include <climits>

int main() {
#ifdef __SANITIZE_ADDRESS__
  std::cout << "Address sanitizer enabled\n";
#else
  std::cout << "Address sanitizer not enabled\n";
#endif

  int x = INT_MAX;
  x += 42;                     // signed integer overflow

  return EXIT_SUCCESS;
}

构建并运行

cd signed_integer_overflow/
conan build . -pr ../profiles/gcc_asan_ubsan
build/Debug/signed_integer_overflow

预期输出(缩写)

Address sanitizer enabled
.../main.cpp:16:9: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'

执行示例应用程序时,UBSan 检测到有符号整数溢出并按预期报告。