AddressSanitizer原理解析

AddressSanitizer: A Fast Address Sanity Checker

GitHub官方仓库
AddressSanitizer论文地址

摘要

内存访问错误,包括缓冲区溢出和释放堆内存的使用,仍然是 C 和 C++ 等编程语言的严重问题。存在许多内存错误检测器,但它们中的大多数要么很慢,要么检测到一组有限的错误,或者两者兼而有之。本文介绍了 AddressSanitizer,一种新的内存错误检测器。我们的工具可以发现对堆、堆栈和全局对象的越界访问,以及释放后使用的错误。它采用了专门的内存分配器和代码工具,这些工具非常简单,可以在任何编译器、二进制翻译系统甚至硬件中实现。 AddressSanitizer 在不牺牲全面性的情况下实现了效率。它的平均减速仅为 73%,但它可以准确地检测到发生点的错误。它在 Chromium 浏览器中发现了 300 多个以前未知的错误,并在其他软件中发现了许多错误。

1. 简介

市场上有数十种内存错误检测工具 这些工具在速度、内存消耗、可检测错误的类型、检测错误的概率、支持的平台和其他特征方面有所不同。许多工具成功地检测到了广泛的错误,但会产生高开销,或者产生低开销但检测到的错误较少。我们介绍了 AddressSanitizer,这是一种结合了性能和覆盖范围的新工具。 AddressSanitizer 发现越界访问(对于堆、堆栈和全局对象)和释放堆内存的使用,成本相对较低,降低了 73%,内存使用量增加了 3.4 倍**,**使其成为测试广泛范围的不错选择的 C/C++ 应用程序。

AddressSanitizer 由两部分组成:

  • 仪表模块

    仪表模块修改代码以检查每次内存访问时候内存对应的影子状态,并创建中毒可读区域围绕堆栈和全局对象来检测溢出和下溢。

  • 运行时库。

    运行时库替换 malloc、free 和相关函数,在分配的堆区域周围创建有毒的红色区域,延迟释放堆区域的重用,并进行错误报告。

1.1 贡献

在本文中,我们 表明内存错误检测器可以利用影子内存的全面性,而开销比传统智慧低得多。

• 提出一种新颖的影子状态编码,它可以实现紧凑的影子内存——多达 128 对 1 的映射——用于检测越界和释放后使用错误;

• 描述一个专门针对我们的影子编码的内存分配器;

• 评估一种可有效识别内存错误的新公开工具。

1.3 大纲

在下一节总结了相关工作之后,我们在第 3 节描述了 AddressSanitizer 算法。第 4 节提供了 AddressSanitizer 的实验结果。我们在第 5 节讨论了进一步的改进,然后总结了论文。

2. 相关工作

本节探讨现有内存检测工具和技术的范围。

2.1 影子内存

许**多不同的工具使用影子内存来存储对应于每条应用程序数据的元数据。**通常,应用程序地址通过直接缩放和偏移映射到影子地址,其中完整的应用程序地址空间映射到单个影子地址空间,

或者通过涉及表查找的额外级别的转换。直接映射的示例包括 TaintTrace [10] 和 LIFT [26]。

TaintTrace 需要与应用程序地址空间大小相等的影子空间,这导致难以支持仅使用其正常地址空间的一半无法生存的应用程序。 LIFT 中的影子空间是应用程序空间的八分之一。为了在地址空间布局中提供更大的灵活性,一些工具使用多级转换方案。

Valgrind [20] 和 Dr. Memory [8] 将他们的影子内存分成几块,并使用表查找来获取影子地址,这需要额外的内存负载。对于 64 位平台,Valgrind 使用额外的表层来存储不在较低 32GB 中的应用程序地址。

Umbra [30, 31] 结合了布局灵活性和效率,避免了通过非均匀和动态调整的比例和偏移方案进行的表查找。

Bound-Less [9] 将其一些元数据存储在 64 位指针的 16 个高位中,但在慢速路径上回退到更传统的影子内存。

LBC [12] 使用存储在应用程序内存中的特殊值执行快速路径检查,并依赖于慢速路径上的两级影子内存。

2.2 仪器仪表

仪器是用于指示、测量和记录物理量的测量仪器的统称。该术语起源于科学仪器制造的艺术和科学。
仪表可以指像直读温度计这样简单的设备,也可以指像工业控制系统的多传感器组件那样复杂的设备。今天,仪器可以在实验室、炼油厂、工厂和车辆以及日常家庭使用中找到(例如,烟雾探测器和恒温器)

**大量内存错误检测器基于二进制检测。**其中最受欢迎的是 Valgrind (Memcheck) [21]、Dr. Memory [8]、Purify[13]、BoundsChecker [17]、Intel Parallel Inspector [15] 和 Discover [23]。

这些工具会发现堆内存的越界和使用后释放错误,(通常)没有误报。

据我们所知,没有一个基于二进制检测的工具可以在堆栈(堆栈顶部之外的除外)或全局变量中找到越界错误。这

些工具还发现未初始化的读取。

Mudflap [11] 使用编译时检测,因此能够检测堆栈对象的越界访问。但是,它不会在一个堆栈帧中的不同堆栈对象之间插入红区,因此不会检测到所有堆栈缓冲区溢出错误。在复杂的 C++ 代码中也存在误报报告。

CCured [19] 将检测与静态分析(仅适用于 C 程序)相结合,以消除冗余检查;他们的检测与未检测的库不兼容。

LBC [12] 使用源到源转换并依靠 CCured 来消除冗余检查。 LBC 仅限于 C 语言,不处理 use-after-free 错误。

Insure++ [24] 主要依赖于编译时检测,但也使用二进制检测。其实施细节未公开

2.3 调试分配器

另一类内存错误检测器使用专门的内存分配器,不会改变其余的执行。

Electric Fence [25]、Duma [3]、GuardMalloc [16] 和 Page Heap [18] 等工具使用 CPU 页面保护。每个分配的区域都放置在一个专用页面(或一组页面)中。右侧(和/或左侧)的一个额外页面被分配并标记为不可访问。访问这些页面的后续页面错误将报告为越界错误。这些工具会产生大量内存开销,并且在 malloc 密集型应用程序上可能非常慢(因为每个 malloc 调用至少需要一个系统调用)。

此外,这些工具可能会遗漏某些类别的错误(例如,从 5 字节内存区域的开头读取偏移 6 处的字节)。如果报告了错误,则会在错误消息中提供负责的说明。其他一些 malloc 实现,包括 DieHarder [22](DieHard [5] malloc 的后代)和 Dmalloc [2],在概率和/或延迟的基础上发现内存错误。他们修改后的 malloc 函数在返回给用户的内存区域周围添加了红区,并用特殊的魔法值填充新分配的内存。 free 函数还将魔法值写入内存区域。如果读取了魔法值,则程序访问了越界或未初始化的值。但是,没有立即检测到这一点。通过正确选择的魔法值,程序有可能以现有应用程序测试可检测到的方式出现错误行为(DieHard [5] 有一个复制模式,它能够通过比较用不同的魔法值初始化的几个程序副本的输出)。换句话说,越界读取和释放后读取错误的检测是概率性的。如果 redzone 中的魔法值被覆盖,稍后将在 redzone 在 free 上检查时检测到这一点,但该工具不知道何时发生越界写入或 write-after-free。对于大型程序,它通常等同于报告“您的程序有一个错误”。请注意,DieHarder 的目标不仅是检测错误,还包括防止安全攻击。这两种调试 malloc 方法经常结合使用。调试 malloc 工具不处理堆栈变量或全局变量。相同的魔法值技术经常用于缓冲区溢出保护。 StackGuard [29] 和 ProPolice [14](GCC 当前使用的 StackGuard 重新实现)在当前堆栈帧中的局部变量和返回地址之间放置一个金丝雀值,并在函数退出时检查该值的一致性。这有助于防止堆栈粉碎缓冲区溢出,但无法检测对堆栈对象的任意越界访问。

3. AddressSanitizer 算法

从高层次上看,我们的内存错误检测方法类似于基于 Valgrind 的工具 AddrCheck [27]:**使用影子内存记录应用程序内存的每个字节是否可以安全访问,并使用检测检查每个应用程序加载或存储的影子内存。**然而,AddressSanitizer 使用更高效的影子映射,更紧凑的影子编码,除了堆之外还检测堆栈和全局变量中的错误,并且比 AddrCheck 快一个数量级。以下部分描述了 AddressSanitizer 如何编码和映射它的影子内存、插入它的检测以及它的运行时库是如何运行的

3.1 影子内存

malloc 函数返回的内存地址通常至少对齐 8 个字节。这导致观察到任何对齐的 8 字节应用程序堆内存序列处于 9 种不同状态之一:前 k (0 ≤ k ≤ 8) 字节是可寻址的,其余 8 - k 字节不可寻址.这种状态可以被编码到影子内存的单个字节中。 AddressSanitizer 将八分之一的虚拟地址空间专用于其影子内存,并使用具有比例和偏移量的直接映射将应用程序地址转换为其相应的影子地址。给定应用程序内存地址 Addr,**影子字节的地址计算为 (Addr>>3)+Offset。**如果 Max-1 是虚拟地址空间中的最大有效地址,则 Offset 的值应选择为在启动时不占用从 Offset 到 Offset+Max/8 的区域。与 Umbra [31] 不同,必须为每个平台静态选择偏移量,但我们不认为这是一个严重的限制。在典型的 32 位 Linux 或 MacOS 系统上,虚拟地址空间为 0x00000000-0xffffffff,

image-20220605213751918

我们使用偏移量 = 0x20000000 (229)。在具有 47 个有效地址位的 64 位系统上,我们使用 Offset = 0x0000100000000000 (244)。在某些情况下(例如,在 Linux 上使用 -fPIE/-pie 编译器标志),可以使用零偏移来进一步简化检测。

**我们对每个影子字节使用以下编码: 0 表示相应应用程序内存区域的所有 8 个字节都是可寻址的; k (1 ≤ k ≤ 7) 表示前k个字节是可寻址的;任何负值表示整个 8 字节字不可寻址。**我们使用不同的负值来区分不同类型的不可寻址内存(堆红区、堆栈红区、全局红区、释放的内存)。这种阴影映射可以推广到 (Addr>>Scale)+Offset 的形式,其中 Scale 是 1 …7 之一。 Scale=N,影子内存占用虚拟地址空间的 1/2N,redzone(和 malloc 对齐)的最小大小为 2N 字节。每个影子字节描述了 2N 个字节的状态并编码了 2N + 1 个不同的值。较大的 Scale 值需要较少的影子内存,但需要较大的红区大小来满足对齐要求。 Scale 值大于 3 需要更复杂的 8 字节访问检测(参见第 3.2 节),但为可能无法放弃单个连续八分之一地址空间的应用程序提供更大的灵活性。

3.2 仪器仪表

在检测 8 字节内存访问时,Address-Sanitizer 计算相应影子字节的地址,加载该字节,并检查它是否为零:

ShadowAddr = (Addr >> 3) + Offset;
if (*ShadowAddr != 0)
ReportAndCrash(Addr);

在检测 1、2 或 4 字节访问时,检测稍微复杂一些:如果影子值为正(即,只有 8 字节字中的前 k 个字节是可寻址的),我们需要比较 3带有 k 的地址的最后一位。

ShadowAddr = (Addr >> 3) + Offset;
k = *ShadowAddr;
if (k != 0 && ((Addr & 7) + AccessSize > k))
ReportAndCrash(Addr);

在这两种情况下,仪器只为原始代码中的每个内存访问插入一个内存读取。我们假设一个 N 字节访问与 N 对齐。Address-Sanitizer 可能会错过由未对齐访问引起的错误,如第 3.5 节所述

我们将 AddressSanitizer 检测通道放置在 LLVM 优化管道的最后。这样,我们只检测那些在 LLVM 优化器执行的所有标量和循环优化中幸存下来的内存访问。例如,对被 LLVM 优化掉的本地堆栈对象的内存访问将不会被检测。同时,我们不必检测 LLVM 代码生成器生成的内存访问(例如,寄存器溢出)。报错代码(ReportAndCrash(Addr))最多执行一次,但在代码中插入了很多地方,所以还是要紧凑。目前我们使用简单的函数调用(参见附录 A 中的示例)。另一种选择是使用产生硬件异常的指令

3.3 运行时库

**运行时库的主要目的是管理影子内存。**在应用程序启动时,整个影子区域都被映射,因此程序的其他部分不能使用它。影子内存的坏段受到保护。在 Linux 上,影子区域在启动时总是未被占用,因此内存映射总是成功的。在 MacOS 上,我们需要禁用地址空间布局随机化 (ASLR)。我们的初步实验表明,相同的影子内存布局也适用于 Windows。

**malloc 和 free 函数被替换为专门的实现。**malloc 函数在返回的区域周围分配额外的内存,即 redzone。红色区域被标记为不可寻址或中毒。红区越大,将检测到的上溢或下溢越大。分配器内的内存区域被组织为一个空闲列表数组,对应于一系列对象大小。当与请求的对象大小相对应的空闲列表为空时,从操作系统(例如,使用 mmap)分配一大组带有 redzones 的内存区域。对于 n 个区域,我们分配 n + 1 个红区,这样一个区域的右侧红区通常是另一个区域的左侧红区

image-20220605214321749

左边的redzone用于存放allocator的内部数据(如分配大小、线程ID等);因此,当前堆 redzone 的最小大小为 32 字节。这个内部数据不会被缓冲区下溢破坏,因为这种下溢是在实际存储之前立即检测到的(如果下溢发生在检测代码中)。 free 函数会毒化整个内存区域并将其置于隔离区,这样该区域就不会很快被 malloc 分配。目前,**隔离区是作为一个 FIFO 队列实现的,它在任何时候都拥有固定数量的内存。**默认情况下,malloc 和 free 记录当前调用堆栈,以便提供更多信息的错误报告。 malloc 调用堆栈存储在左侧 redzone 中(redzone 越大,可以存储的帧数越多),而 free 调用堆栈存储在内存区域本身的开头。 4.3 节讨论如何调整运行时库。

3.4 堆栈和全局变量

为了检测对全局和堆栈对象的越界访问,AddressSanitizer 必须在这些对象周围创建有毒的红色区域。对于全局变量,红区是在编译时创建的,红区的地址在应用程序启动时传递给运行时库。运行时库函数会毒化红区并记录地址以供进一步错误报告。对于堆栈对象,红区是在运行时创建和毒化的。目前,使用 32 字节的红区(加上最多 31 字节用于对齐)。例如,给定一个程序

void foo() {
char a[10];
<function body> }

转换后的代码看起来像

void foo() {
char rz1[32]
char arr[10];
char rz2[32-10+32];
unsigned *shadow =
(unsigned*)(((long)rz1>>8)+Offset);
// poison the redzones around arr.
shadow[0] = 0xffffffff; // rz1
shadow[1] = 0xffff0200; // arr and rz2
shadow[2] = 0xffffffff; // rz2
<function body>
// un-poison all.
shadow[0] = shadow[1] = shadow[2] = 0; }

3.5 假负值

上述检测方案可能会遗漏一种非常罕见的错误类型:部分越界的未对齐访问。例如:

int *a = new int[2]; // 8-aligned
int *u = (int*)((char*)a + 6);
*u = 1; // Access to range [6-9]

目前我们忽略了这种类型的错误,因为我们提出的所有解决方案都会减慢通用路径。我们考虑的解决方案包括

• 在运行时检查地址是否未对齐;

• 使用字节到字节的影子映射(仅在 64 位系统上可行);

• 使用更紧凑的映射(例如,第 3.1 节中的 Scale=7)以最小化遗漏此类错误的概率。

在以下两种情况下,AddressSanitizer 也可能会遗漏错误(Valgrind 或 Dr. Memory 等工具也有同样的问题)。首先,如果越界访问触及距离对象边界太远的内存,它可能会落在不同的有效分配中,并且错误将被遗漏。

char *a = new char[100];
char *b = new char[1000];
a[500] = 0; // may end up somewhere in b

堆红区内的所有越界访问都将以 100% 的概率被检测到。如果内存占用不是一个严重的限制,我们建议使用高达 128 字节的大红区

其次,如果在“free”和后续使用之间分配和释放了大量内存,则可能无法检测到 use-after-free。

char *a = new char[1 << 20]; // 1MB
delete [] a; // <<< "free"
char *b = new char[1 << 28]; // 256MB
delete [] b; // drains the quarantine queue.
char *c = new char[1 << 20]; // 1MB
a[0] = 0; // "use". May land in ’c’.

3.6 误报

简而言之,AddressSanitizer 没有误报。然而,在 AddressSanitizer 开发和部署过程中,我们看到了下面描述的许多不良错误报告,现在所有这些都已修复

略~!!!

3.7 线程

**AddressSanitizer 是线程安全的。**只有在相应的应用程序内存不可访问时(在 malloc 或 free 内,在创建或销毁堆栈帧期间,在模块初始化期间),才会修改影子内存。对影子内存的所有其他访问都是读取。 malloc 和 free 函数使用线程本地缓存来避免锁定每次调用(就像大多数现代 malloc 实现一样)。如果原始程序在内存访问和删除该内存之间存在竞争,AddressSanitizer 有时可能会将其检测为释放后使用错误,但不能保证如此。为每个 malloc 和 free 记录线程 ID,并在错误消息中与线程创建调用堆栈一起报告。

4.评估

我们在 SPEC CPU2006 [28] 的 C 和 C++ 基准测试中测量了 AddressSanitizer 的性能。测量是在配备 2 个四核 Intel Xeon E5620 CPU 和 24GB RAM 的 HP Z600 机器上以 64 位模式完成的。我们将检测二进制文件的性能与使用常规 LLVM 编译器 (clang -O2) 构建的二进制文件的性能进行了比较。我们使用了 32 字节的红色区域,在 malloc 和 free 期间禁用了堆栈展开,并将隔离区大小设置为零(参见第 4.3 节)。

图 2 显示 CPU2006 的平均减速率为 73%。 perlbench 和 xalancbmk 的降幅最大(分别为 2.60 倍和 2.67 倍)。

这两个基准测试是 malloc 密集型的,并且会进行大量的 1 字节和 2 字节内存访问(两个基准测试都是文本处理程序)。

我们还测量了仅检测写入时的 AddressSanitizer 性能:平均减速为 26%。这种模式可以在性能关键的环境中使用,以查找内存错误的子集。

image-20220605215152875

在 CPU2006 中发现了三个 bug:h264ref 中的一个堆栈和一个全局缓冲区溢出,以及 perlbench 中的 use-after-realloc

我们还评估了不同映射比例和偏移值的性能(见第 3.1 节)。大于 3 的 Scale 值平均会产生稍慢的代码(与 Scale=3 相比,从 2% 加速到 15% 减速)。 Scale=4,5 的内存占用接近 Scale=3。对于值 6 和 7,内存占用更大,因为需要更大的红色区域。将 Offset 设置为零(这需要 -fPIE/-pie)会提供小幅加速,使 CPU2006 的平均减速达到 69%。表 1 总结了内存使用的增加(通过在进程终止时从 /proc/self/status 读取 VmPeak 字段收集)。内存开销主要来自 malloc 红色区域。平均内存使用量增加了 3.37 倍。隔离还有一个固定大小的开销,我们在实验中没有计算在内。表 2 总结了堆栈大小的增加(/proc/self/status 中的 VmStk 字段)。只有 6 个基准测试有明显的堆栈大小变化,只有 3 个基准测试的堆栈大小增加超过 10%。 SPEC CPU2006 上的二进制大小增加范围从 1.5 倍到 3.2 倍,平均为 2.5 倍。

4.1 比较

将 AddressSanitizer 与其他工具进行比较是很棘手的,因为其他工具会发现不同的错误集。 Valgrind 和 Dr. Memory 在 CPU2006 上分别导致 20 倍和 10 倍的减速 [8]。但是这些工具检测到一组不同的错误(除了越界和释放后使用,它们检测未初始化的读取和内存泄漏,但不处理大多数堆栈变量和全局变量的越界) . Mudflap,可能是与 Address-Sanitizer 最相似的工具,具有非常不同寻常的性能特征。根据我们的测量,Mudflap 在 CPU2006 上的减速范围从 2 倍到 41 倍;一些基准测试因内存不足错误而失败。调试使用 CPU 保护页的 malloc 实现通常只会减慢 malloc 密集型应用程序的速度。用于 Linux 的免费保护页面实现 Duma 在 18 次中的 12 次崩溃

4.2 AddressSanitizer 部署

自从我们在 2011 年 5 月发布该工具以来,Chromium 开源浏览器 [1] 已定期使用 AddressSanitizer 进行测试。在测试的前 10 个月中,该工具在 Chromium 代码和第三方中检测到 300 多个以前未知的错误图书馆。 210 个 bug 是 heap-use-after-free,73 个是 heap-buffer-overflow,8 个 global-buffer-overflow,7 个 stack-buffer-overflow 和 1 个 memcpy 参数重叠。在另外 13 个案例中,Address-Sanitizer 触发了一些其他类型的程序错误(例如,未初始化的内存读取),但没有提供有意义的错误消息。 Chromium 中错误报告的两个主要来源是现有单元测试的定期运行和有针对性的随机测试生成(模糊测试)。在任何一种情况下,检测代码的速度都是至关重要的。对于单元测试,高速允许使用更少的机器来跟上源代码的变化。对于模糊测试,它允许运行只需几秒钟的随机测试(因为 AddressSanitizer 是作为编译时工具实现的,它没有启动惩罚),一旦发现错误,在合理的时间内最小化测试。通过手动运行已检测的浏览器发现了少量错误——这对于速度明显较慢的工具是不可能的。除了 Chromium,我们还测试了大量其他代码,发现了很多错误。和 Chromium 一样,heap-use-after-free 是最常见的 bug。然而,堆栈溢出和全局缓冲区溢出比 Chromium 更常见。在 LLVM 本身中检测到几个 heap-use-after-free 错误。我们收到了有关 AddressSanitizer 在 Firefox、Perl、Vim 和其他几个开源项目中发现的错误的通知3

4.3 调整精度和资源使用

AddressSanitizer 具有三个影响准确性和资源使用的主要控件。

  • 堆栈展开的深度(默认值:30)。

    在每次调用 malloc 和 free 时,该工具都需要展开调用堆栈,以便错误消息包含更多信息。此选项会影响工具的速度,尤其是在测试的应用程序是 malloc 密集型的情况下。它不会影响内存占用或错误发现能力,但短的堆栈跟踪通常不足以分析错误消息。

  • 隔离区大小(默认值:256MB)。

    该值控制查找释放后堆使用错误的能力(参见第 3.5 节)。它不影响性能。

  • 堆 redzone 的大小(默认值:128 字节)。
    此选项会影响查找堆缓冲区溢出错误的能力(请参阅第 3.5 节)。较大的值可能会导致显着减慢和内存使用量增加,特别是如果测试程序分配了许多小堆内存块。由于 redzone 用于存储 malloc 调用堆栈,因此减少 redzone 会自动降低最大展开深度。

在测试 Chromium 时,我们使用了这三个参数的默认值。增加其中任何一个都不会增加错误发现能力。在测试其他软件时,我们有时不得不使用较小的 redzone 大小(32 或 64 字节)和/或完全禁用堆栈展开以满足极端的内存和速度限制。在具有少量 RAM 的环境中,我们使用较小的隔离区大小。所有三个值都由环境变量控制,可以在进程启动时设置

总结

  1. AddressSanitizer 发现越界(对于堆、堆栈和全局)访问和释放后使用平均速度降低 73% 的错误;

  2. 该工具没有误报。

  3. AddressSanitizer 使用影子内存来提供准确和即时的错误检测。传统观点认为,影子内存要么通过多级映射方案产生高开销,要么通过占用大的连续区域来强加令人望而却步的地址空间要求。我们新颖的阴影状态编码减少了我们的阴影空间占用,我们可以使用简单的映射,它可以以低开销实现。该工具提供的高速允许用户更快地运行更多测试。

  4. 该工具已用于测试 Chromium 浏览器,并在短短 10 个月内发现了 300 多个真正的错误,其中包括一些可能导致安全漏洞的错误。 AddressSanitizer 用户在 Firefox、Perl、Vim 和 LLVM 中发现了错误。 AddressSanitizer 所需的工具非常简单,可以在各种编译器、二进制工具系统甚至硬件中实现。

结束


  • Instrumentation module

  • run-time library

  • shadow memery

我们将展示两个基于编译时工具的动态测试工具,这两个工具都使用 LLVM 编译器。

  • AddressSanitizer (ASan) 发现内存错误,例如释放后使用和对堆和堆栈的越界访问。这个工具可以被看作是 Valgrind 和类似工具的部分替代品。与 Valgrind 相比的主要优势是速度(平均低于 2 倍)以及处理与堆栈和全局相关的错误的能力。
  • ThreadSanitizer (TSan) 发现数据竞争。它使用与基于 Valgrind 的 TSan 相同的竞争检测算法,但编译时检测允许它更快(2x-4x 减速)。

在cmake中使用

直接使用

  • cmake
    set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
    set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
  • command
clang++ -O1 -g -fsanitize=address -fno-omit-frame-pointer example_UseAfterFree.cc

使用脚本

function(add_sanitizer_flags)
    if(NOT ENABLE_SANITIZE_ADDR AND NOT ENABLE_SANITIZE_UNDEF)
        return()
    endif()

    if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES
                                                "GNU")
        add_compile_options("-fno-omit-frame-pointer")
        add_link_options("-fno-omit-frame-pointer")

        if(ENABLE_SANITIZE_ADDR)
            add_compile_options("-fsanitize=address")
            add_link_options("-fsanitize=address")
        endif()

        if(ENABLE_SANITIZE_UNDEF)
            add_compile_options("-fsanitize=undefined")
            add_link_options("-fsanitize=undefined")
        endif()

        if(ENABLE_SANITIZE_LEAK)
            add_compile_options("-fsanitize=leak")
            add_link_options("-fsanitize=leak")
        endif()

        if(ENABLE_SANITIZE_THREAD)
            if(ENABLE_SANITIZE_ADDR OR ENABLE_SANITIZE_LEAK)
                message(WARNING "thread does not work with: address and leak")
            endif()
            add_compile_options("-fsanitize=thread")
            add_link_options("-fsanitize=thread")
        endif()
    elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
        if(ENABLE_SANITIZE_ADDR)
            add_compile_options("/fsanitize=address")
        endif()

        if(ENABLE_SANITIZE_UNDEF)
            message(STATUS "sanitize=undefined not avail. for MSVC")
        endif()

        if(ENABLE_SANITIZE_LEAK)
            message(STATUS "sanitize=leak not avail. for MSVC")
        endif()

        if(ENABLE_SANITIZE_THREAD)
            message(STATUS "sanitize=thread not avail. for MSVC")
        endif()
    else()
        message(WARNING "This sanitizer not supported in this environment")
        return()
    endif()
endfunction(add_sanitizer_flags)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

早睡的叶子

你的鼓励就是我的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值