漏洞利用缓解措施
本章记录了 Rust 编译器支持的漏洞利用缓解措施,绝非对 Rust 编程语言安全功能的全面调查。
本章适用于使用 Rust 编程语言的软件工程师,并假设读者事先了解 Rust 编程语言及其工具链。
简介
Rust 编程语言通过其所有权 [3]、引用和借用 [4] 以及切片类型 [5] 等特性提供内存 [1] 和线程 [2] 安全保证。然而,Unsafe Rust [6] 引入了 unsafe 代码块、unsafe 函数和方法、unsafe 特性和不受借用规则约束的新类型。
Rust 标准库的部分实现为 unsafe 代码之上的安全抽象(并且历史上容易受到内存损坏的影响 [7])。此外,Rust 代码和文档鼓励在 unsafe 代码之上创建安全抽象。如果 unsafe 代码没有经过适当的审查和测试,这可能会导致一种虚假的安全感。
Unsafe Rust 引入了不提供相同内存和线程安全保证的特性。这会导致程序或库容易受到内存损坏(CWE-119)[8] 和并发问题(CWE-557)[9] 的影响。现代 C 和 C++ 编译器提供了漏洞利用缓解措施,以增加利用这些问题导致的漏洞的难度。因此,Rust 编译器也必须支持这些漏洞利用缓解措施,以缓解使用 Unsafe Rust 导致的漏洞。本章记录了这些漏洞利用缓解措施以及它们如何应用于 Rust。
本章不讨论这些漏洞利用缓解措施的有效性,因为它们的有效性除了设计和实现之外,还因多种因素而异,而是描述它们的作用,以便在给定的环境中理解它们的有效性。
漏洞利用缓解措施
本节记录了当在 AMD64 架构及等效架构的 Linux 操作系统上构建程序时适用于 Rust 编译器的漏洞利用缓解措施。1本节中的所有示例都是使用 Debian 测试版上的 Rust 编译器的 nightly 版本构建的。
Rust 编程语言目前没有规范。Rust 编译器(即 rustc)是该语言的参考实现。本章中所有对“Rust 编译器”的引用均指语言的参考实现。
表 I
Rust 编译器在 AMD64 架构及等效架构的 Linux 操作系统上构建程序时支持的漏洞利用缓解措施摘要。
漏洞利用缓解措施 | 默认支持并启用 | 自从 |
---|---|---|
位置无关可执行文件 | 是 | 0.12.0 (2014-10-09) |
整数溢出检查 | 是(启用调试断言时启用,禁用调试断言时禁用) | 1.1.0 (2015-06-25) |
不可执行内存区域 | 是 | 1.8.0 (2016-04-14) |
堆栈冲突保护 | 是 | 1.20.0 (2017-08-31) |
只读重定位和立即绑定 | 是 | 1.21.0 (2017-10-12) |
堆损坏保护 | 是 | 1.32.0 (2019-01-17)(通过操作系统默认或指定分配器) |
堆栈粉碎保护 | 是 | Nightly |
前向边缘控制流保护 | 是 | Nightly |
后向边缘控制流保护(例如,影子和安全堆栈) | 是 | Nightly |
1. 有关目标及其默认选项的列表,请参见 https://github.com/rust-lang/rust/tree/master/compiler/rustc_target/src/spec。 ↩
位置无关可执行文件
位置无关可执行文件通过为可执行文件生成位置无关代码,并指示动态链接器以类似于共享对象的方式在随机加载地址加载它,从而增加代码重用利用技术(例如面向返回的编程 (ROP) 及其变体)的难度,从而也受益于地址空间布局随机化 (ASLR)。这也称为“完全 ASLR”。
Rust 编译器支持位置无关可执行文件,并且自 0.12.0 版本(2014-10-09)[10]–[13] 以来默认启用。
$ readelf -h target/release/hello-rust | grep Type:
Type: DYN (Shared object file)
图 1. 检查可执行文件是否为位置无关可执行文件。
对象类型为 ET_DYN
(即共享对象)而不是 ET_EXEC
(即可执行文件)的可执行文件是位置无关可执行文件(请参阅图 1)。
整数溢出检查
整数溢出检查通过检查带符号和无符号整数计算的结果是否不能以它们的类型表示,从而导致溢出或回绕,从而保护程序免受未定义和意外行为(可能导致漏洞)的影响。
Rust 编译器支持整数溢出检查,并且自 1.0.0 版本(2015-05-15)[14]–[17] 起在启用调试断言时启用,但对它的支持直到 1.1.0 版本(2015-06-25)[16] 才完成。后来在 1.17.0 版本(2017-04-27)[18]–[20] 中稳定了一个控制整数溢出检查的选项。
fn main() {
let u: u8 = 255;
println!("u: {}", u + 1);
}
图 2. hello-rust-integer 程序。
$ cargo run
Compiling hello-rust-integer v0.1.0 (/home/rcvalle/hello-rust-integer)
Finished dev [unoptimized + debuginfo] target(s) in 0.23s
Running `target/debug/hello-rust-integer`
thread 'main' panicked at 'attempt to add with overflow', src/main.rs:3:23
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
图 3. 启用调试断言时 hello-rust-integer 的构建和执行。
$ cargo run --release
Compiling hello-rust-integer v0.1.0 (/home/rcvalle/hello-rust-integer)
Finished release [optimized] target(s) in 0.23s
Running `target/release/hello-rust-integer`
u: 0
图 4. 禁用调试断言时 hello-rust-integer 的构建和执行。
启用调试断言时会启用整数溢出检查(请参阅图 3),禁用调试断言时会禁用整数溢出检查(请参阅图 4)。要独立启用整数溢出检查,请使用控制整数溢出检查的选项、范围属性或显式检查方法,例如 checked_add
2。
建议在需要回绕语义时使用显式回绕方法(例如 wrapping_add
),并且在使用 Unsafe Rust 时始终使用显式检查和回绕方法。
2. 有关 checked、overflowing、saturating 和 wrapping 方法的更多信息(以 u32 为例),请参阅u32
文档。↩
不可执行内存区域
不可执行内存区域通过限制可用于执行任意代码的内存区域来增加利用的难度。大多数现代处理器都支持操作系统将内存区域标记为不可执行,但以前由软件模拟,例如 grsecurity/PaX 的PAGEEXEC 和SEGMEXEC,在不支持它的处理器上。这也称为“No Execute (NX) Bit”、“Execute Disable (XD) Bit”、“Execute Never (XN) Bit”等。
Rust 编译器支持不可执行内存区域,并且自其初始版本 0.1(2012-01-20)[21]、[22] 以来默认启用,但此后已退化 [23]–[25],并自 1.8.0 版本(2016-04-14)[25] 以来默认强制执行。
$ readelf -l target/release/hello-rust | grep -A 1 GNU_STACK
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
图 5. 检查是否为给定的二进制文件启用了不可执行内存区域。
程序头表中存在类型为 PT_GNU_STACK
且 PF_X
(即可执行)标志未设置的元素表示为给定的二进制文件启用了不可执行内存区域3(请参阅图 5)。相反,程序头表中存在类型为 PT_GNU_STACK
且 PF_X
标志已设置的元素,或者程序头表中不存在类型为 PT_GNU_STACK
的元素,表示为给定的二进制文件未启用不可执行内存区域。
3. 有关为什么它会影响堆栈之外的其他内存区域的更多信息,请参见附录部分。↩
堆栈冲突保护
堆栈冲突保护通过在堆栈增长时从堆栈页面读取来保护堆栈与另一个内存区域重叠(允许使用彼此覆盖两者中的任意数据),从而在尝试从保护页/区域读取时导致页面错误。这也称为“堆栈探测”或“堆栈探测”。
Rust 编译器通过堆栈探测支持堆栈冲突保护,并且自 1.20.0 版本(2017-08-31)[26]–[29] 以来默认启用。
fn main() {
let v: [u8; 16384] = [1; 16384];
let first = &v[0];
println!("The first element is: {first}");
}
图 6. hello-rust-stack-probe-1 程序。
图 7. 修改后的 hello-rust 中的“展开循环”堆栈探测变体。
fn main() {
let v: [u8; 65536] = [1; 65536];
let first = &v[0];
println!("The first element is: {first}");
}
图 8. hello-rust-stack-probe-2 程序。
图 9. 修改后的 hello-rust 中的“标准循环”堆栈探测变体。
要检查是否为给定的二进制文件启用了堆栈冲突保护,请查找堆栈大小大于页面大小的函数的序言中的两个堆栈探测变体中的任何一个(请参阅图 6–9)。
只读重定位和立即绑定
只读重定位通过将这些段标记为只读来保护包含重定位和重定位信息(即 .init_array
、.fini_array
、.dynamic
和 .got
)的段不被覆盖。这也称为“部分 RELRO”。
Rust 编译器支持只读重定位,并且自 1.21.0 版本(2017-10-12)[30]、[31] 以来默认启用。
$ readelf -l target/release/hello-rust | grep GNU_RELRO
GNU_RELRO 0x000000000002ee00 0x000000000002fe00 0x000000000002fe00
图 9. 检查是否为给定的二进制文件启用了只读重定位。
程序头表中存在类型为 PT_GNU_RELRO
的元素表示为给定的二进制文件启用了只读重定位(请参阅图 9)。相反,程序头表中不存在类型为 PT_GNU_RELRO
的元素表示为给定的二进制文件未启用只读重定位。
立即绑定通过指示动态链接器在启动期间将控制权转移到程序之前执行所有重定位,从而保护包含重定位(即 .got.plt
)的其他段不被覆盖,以便可以将所有包含重定位的段标记为只读(当与只读重定位结合使用时)。这也称为“完全 RELRO”。
Rust 编译器支持立即绑定,并且自 1.21.0 版本(2017-10-12)[30]、[31] 以来默认启用。
$ readelf -d target/release/hello-rust | grep BIND_NOW
0x000000000000001e (FLAGS) BIND_NOW
图 10. 检查是否为给定的二进制文件启用了立即绑定。
动态节中存在带有 DT_BIND_NOW
标记和 DF_BIND_NOW
标志4 的元素表示为给定的二进制文件启用了立即绑定(请参阅图 10)。相反,动态节中不存在带有 DT_BIND_NOW
标记和 DF_BIND_NOW
标志的元素表示为给定的二进制文件未启用立即绑定。
程序头表中存在类型为 PT_GNU_RELRO
的元素,且动态节中存在带有 DT_BIND_NOW
标记和 DF_BIND_NOW
标志的元素,表示为给定的二进制文件启用了完全 RELRO(请参阅图 9–10)。
4. 对于某些链接编辑器,还使用 DF_1_NOW
标志。↩
堆损坏保护
堆损坏保护通过执行多项检查来保护动态分配的内存,例如列表元素之间损坏的链接、无效指针、无效大小、相同分配内存的重复/多次“释放”以及这些的许多极端情况。这些检查是特定于实现的,并且因分配器而异。
ARM 内存标记扩展 (MTE)(如果可用)将为检测内存安全违规行为提供硬件辅助的概率性缓解措施,方法是标记内存分配,并自动检查每次内存访问时是否使用了正确的标记。
Rust 的默认内存分配器在历史上一直是 jemalloc,长期以来它一直是问题的根源,并引发了大量的讨论[32]–[38]。因此,自 1.32.0 版本(2019-01-17)[39]以来,它已被移除作为默认分配器,取而代之的是操作系统的标准 C 库默认分配器5。
fn main() {
let mut x = Box::new([0; 1024]);
for i in 0..1026 {
unsafe {
let elem = x.get_unchecked_mut(i);
*elem = 0x4141414141414141u64;
}
}
}
图 11. hello-rust-heap 程序。
$ cargo run
Compiling hello-rust-heap v0.1.0 (/home/rcvalle/hello-rust-heap)
Finished dev [unoptimized + debuginfo] target(s) in 0.25s
Running `target/debug/hello-rust-heap`
free(): invalid next size (normal)
Aborted
图 12. 启用调试断言时构建和执行 hello-rust-heap。
$ cargo run --release
Compiling hello-rust-heap v0.1.0 (/home/rcvalle/hello-rust-heap)
Finished release [optimized] target(s) in 0.25s
Running `target/release/hello-rust-heap`
free(): invalid next size (normal)
Aborted
图 13. 禁用调试断言时构建和执行 hello-rust-heap。
当使用默认分配器(即 GNU 分配器)时,会执行堆损坏检查(见图 12–13)。
5. Linux 的标准 C 库默认分配器是 GNU 分配器,它源自 Wolfram Gloger 的 ptmalloc (pthreads malloc),而 ptmalloc 又源自 Doug Lea 的 dlmalloc (Doug Lea malloc)。 ↩
堆栈粉碎保护
栈溢出保护通过在局部变量和保存的返回指令指针之间插入一个随机保护值,并在从函数返回时检查该值是否已更改,来保护程序免受基于栈的缓冲区溢出攻击。这也被称为“栈保护器”或“栈粉碎保护器 (SSP)”。
Rust 编译器在 nightly 构建版本上支持栈溢出保护[40]。
图 14. IDA Pro 列出了 hello-rust 中对
__stack_chk_fail
的交叉引用。
要检查是否为给定二进制文件启用了栈溢出保护,请搜索对 __stack_chk_fail
的交叉引用(见图 14)。
前向边缘控制流保护
前向边缘控制流保护通过执行检查以确保间接分支的目标是控制流图中其有效目标之一,来保护程序免受控制流更改/劫持。这些检查的全面性因实现而异。这也被称为“前向边缘控制流完整性 (CFI)”。
较新的处理器为前向边缘控制流保护提供了硬件辅助,例如 ARM 分支目标标识 (BTI)、ARM 指针身份验证和作为 Intel 控制流强制技术 (CET) 一部分的 Intel 间接分支跟踪 (IBT)。然而,基于 ARM BTI 和 Intel IBT 的实现不如基于软件的实现(例如 LLVM ControlFlowIntegrity (CFI))和商业可用的 grsecurity/PaX 重用攻击保护器 (RAP) 那样全面。
Rust 编译器在 nightly 构建版本上支持前向边缘控制流保护[41]-[42] 6。
$ readelf -s -W target/release/hello-rust | grep "\.cfi"
5: 0000000000006480 657 FUNC LOCAL DEFAULT 15 _ZN10hello_rust4main17h4e359f1dcd627c83E.cfi
图 15. 检查是否为给定的二进制文件启用了 LLVM CFI。
带有“.cfi”后缀的符号或 __cfi_init
符号(以及对 __cfi_check
的引用)的存在表明已为给定二进制文件启用了 LLVM CFI(即前向边缘控制流保护)。相反,缺少带有“.cfi”后缀的符号或 __cfi_init
符号(以及对 __cfi_check
的引用)表明未为给定二进制文件启用 LLVM CFI(见图 15)。
6. 它还支持 Windows 上的控制流保护 (CFG)(见 https://github.com/rust-lang/rust/issues/68793)。 ↩
后向边缘控制流保护
影子堆栈 通过将保存的返回指令指针的副本存储在单独的(影子)堆栈上,并在从函数返回时使用这些副本作为权威值,来防止它们被覆盖。这也被称为“ShadowCallStack”和“返回流保护”,并被认为是后向边缘控制流保护(或“后向边缘 CFI”)的实现。
安全堆栈 不仅保护保存的返回指令指针,还通过将不安全变量(例如大型数组)存储在单独的(不安全)堆栈上,并在单独的堆栈上使用这些不安全变量,来防止它们被覆盖,同时还保护了寄存器溢出和一些局部变量。这也被称为“SafeStack”,并且也被认为是后向边缘控制流保护的实现。
影子堆栈和安全堆栈都旨在成为栈溢出保护的更全面的替代方案,因为它们可以保护保存的返回指令指针(以及安全堆栈情况下的其他数据)免受任意写入和非线性越界写入的影响。
较新的处理器为后向边缘控制流保护提供了硬件辅助,例如 ARM 指针身份验证和作为 Intel CET 一部分的 Intel 影子堆栈。
Rust 编译器在 nightly 构建版本上支持 AArch64 架构7的影子堆栈[43]-[44],并且还支持 nightly 构建版本的安全堆栈[45]-[46]。
$ readelf -s target/release/hello-rust | grep __safestack_init
678: 0000000000008c80 426 FUNC GLOBAL DEFAULT 15 __safestack_init
图 16. 检查是否为给定的二进制文件启用了 LLVM SafeStack。
__safestack_init
符号的存在表明已为给定二进制文件启用了 LLVM SafeStack。相反,缺少 __safestack_init
符号表明未为给定二进制文件启用 LLVM SafeStack(见图 16)。
7. 由于性能和安全问题,删除了 AMD64 架构的影子堆栈实现以及 LLVM 中的等效实现。 ↩
附录
根据 Linux 标准库 (LSB) 核心规范的最新版本,PT_GNU_STACK
程序头指示堆栈是否应该可执行,而缺少此头则指示堆栈应该可执行。但是,Linux 内核当前在加载任何带有 PT_GNU_STACK
程序头和 PF_X
标志设置或缺少此头的可执行文件时都会设置 READ_IMPLIES_EXEC
个性,从而导致不仅堆栈,而且所有可读虚拟内存映射都可执行。
在 2012 年尝试修复此问题,并在 2020 年再次尝试。前者从未实施,后者部分修复了它,但引入了其他问题——缺少 PT_GNU_STACK
程序头仍然会导致不仅堆栈,而且所有可读虚拟内存映射在某些架构(例如 IA-32 和等效架构)中都可执行(或者导致堆栈在某些架构(例如 AMD64 和等效架构)中不可执行,这与 LSB 相矛盾)。
READ_IMPLIES_EXEC
个性需要与 PT_GNU_STACK
程序头完全分离,方法是为其设置一个单独的选项(或者在需要 READ_IMPLIES_EXEC
时可以使用 setarch -X),并且缺少 PT_GNU_STACK
程序头需要具有更安全的默认值(与 READ_IMPLIES_EXEC
无关)。
参考
-
D. Hosfelt。“无畏的安全:内存安全。” Mozilla Hacks. https://hacks.mozilla.ac.cn/2019/01/fearless-security-memory-safety/。
-
D. Hosfelt。“无畏的安全:线程安全。” Mozilla Hacks. https://hacks.mozilla.ac.cn/2019/02/fearless-security-thread-safety/。
-
S. Klabnik 和 C. Nichols。“什么是所有权?。” Rust 编程语言。 https://doc.rust-lang.net.cn/book/ch04-01-what-is-ownership.html。
-
S. Klabnik 和 C. Nichols。“引用和借用。” Rust 编程语言。 https://doc.rust-lang.net.cn/book/ch04-02-references-and-borrowing.html。
-
S. Klabnik 和 C. Nichols。“切片类型。” Rust 编程语言。 https://doc.rust-lang.net.cn/book/ch04-03-slices.html。
-
S. Klabnik 和 C. Nichols。“不安全的 Rust。” Rust 编程语言。 https://doc.rust-lang.net.cn/book/ch19-01-unsafe-rust.html。
-
S. Davidoff。“Rust 的标准库如何多年来一直存在漏洞而无人知晓。” Medium. https://medium.com/@shnatsel/how-rusts-standard-library-was-vulnerable-for-years-and-nobody-noticed-aebf0503c3d6。
-
“内存缓冲区范围内操作的不正确限制 (CWE-119)。” MITRE CWE 列表。 https://cwe.mitre.org/data/definitions/119.html。
-
“并发问题 (CWE-557)。” MITRE CWE 列表。 https://cwe.mitre.org/data/definitions/557.html。
-
K. McAllister。“内存漏洞利用缓解 #15179。” GitHub. https://github.com/rust-lang/rust/issues/15179。
-
K. McAllister。“RFC:内存漏洞利用缓解 #145。” GitHub. https://github.com/rust-lang/rfcs/pull/145。
-
K. McAllister。“RFC:内存漏洞利用缓解。” GitHub. https://github.com/kmcallister/rfcs/blob/hardening/active/0000-memory-exploit-mitigation.md。
-
D. Micay。“在 Linux 上默认启用 PIE 以实现完整的 ASLR #16340。” GitHub. https://github.com/rust-lang/rust/pull/16340。
-
N. Matsakis。“整数溢出 #560。” GitHub. https://github.com/rust-lang/rfcs/pull/560。
-
G. Lehel 和 N. Matsakis。“整数溢出。” GitHub. https://rust-lang.github.io/rfcs/0560-integer-overflow.html。
-
A. Turon。“整数溢出 (RFC 560) 的跟踪问题 #22020。” GitHub. https://github.com/rust-lang/rust/issues/22020。
-
H. Wilson。“关于 Rust 中整数溢出的神话和传说。” Huon on the Internet. http://huonw.github.io/blog/2016/04/myths-and-legends-about-integer-overflow-in-rust/。
-
B. Anderson。“稳定 -C 溢出检查 #1535。” GitHub. https://github.com/rust-lang/rfcs/pull/1535。
-
B. Anderson。“稳定的溢出检查。” GitHub. https://github.com/brson/rfcs/blob/overflow/text/0000-stable-overflow-checks.md。
-
N. Froyd。“添加 -C 溢出检查选项 #40037。” GitHub. https://github.com/rust-lang/rust/pull/40037。
-
R. Á. de Espíndola。“rustc 需要可执行堆栈 #798。” GitHub. https://github.com/rust-lang/rust/issues/798。
-
A. Seipp。“确保 librustrt.so 链接到不可执行堆栈。#1066。” GitHub. https://github.com/rust-lang/rust/pull/1066。
-
D. Micay。“Rust 二进制文件不应具有可执行堆栈 #5643。” GitHub. https://github.com/rust-lang/rust/issues/5643。
-
D. Micay。“将汇编对象栈标记为不可执行 #5647。” GitHub。 https://github.com/rust-lang/rust/pull/5647。
-
A. Clark。“在 Linux 和 BSD 上显式禁用堆栈执行 #30859。” GitHub。 https://github.com/rust-lang/rust/pull/30859。
-
Zoxc。“用堆栈探测替换堆栈溢出检查 #16012。” GitHub。 https://github.com/rust-lang/rust/issues/16012。
-
A. Crichton。“rustc:为 x86 实现堆栈探测 #42816。” GitHub。 https://github.com/rust-lang/rust/pull/42816。
-
A. Crichton。“添加 __rust_probestack 内联函数 #175。” GitHub。 https://github.com/rust-lang/compiler-builtins/pull/175。
-
S. Guelton、S. Ledru、J. Stone。“将堆栈冲突保护引入 Clang / X86 — 开源方式。” The LLVM Project Blog。 https://blog.llvm.net.cn/posts/2021-01-05-stack-clash-protection/。
-
B. Anderson。“考虑默认应用 -Wl,-z,relro 或 -Wl,-z,relro,-z,now #29877。” GitHub。 https://github.com/rust-lang/rust/issues/29877。
-
J. Löthberg。“添加对完整 RELRO 的支持 #43170。” GitHub。 https://github.com/rust-lang/rust/pull/43170。
-
N. Matsakis。“Rust 中的分配器。” Baby Steps。 http://smallcultfollowing.com/babysteps/blog/2014/11/14/allocators-in-rust/。
-
A. Crichton。“RFC:允许更改默认分配器 #1183。” GitHub。 https://github.com/rust-lang/rfcs/pull/1183。
-
A. Crichton。“RFC:替换 jemalloc。” GitHub。 https://rust-lang.github.io/rfcs/1183-swap-out-jemalloc.html。
-
A. Crichton。“更改全局默认分配器 (RFC 1974) 的跟踪问题 #27389。” GitHub。 https://github.com/rust-lang/rust/issues/27389。
-
S. Fackler。“为稳定化准备全局分配器 #1974。” GitHub。 https://github.com/rust-lang/rfcs/pull/1974。
-
A. Crichton。“RFC:全局分配器。” GitHub。 https://rust-lang.github.io/rfcs/1974-global-allocators.html。
-
B. Anderson。“将默认全局分配器切换为 System,移除 alloc_jemalloc,在 rustc 中使用 jemallocator #36963。” GitHub。 https://github.com/rust-lang/rust/issues/36963。
-
A. Crichton。“移除 alloc_jemalloc crate #55238。” GitHub。 https://github.com/rust-lang/rust/pull/55238。
-
bbjornse。“添加使用 LLVM 堆栈粉碎保护的代码生成选项 #84197。” GitHub。 https://github.com/rust-lang/rust/pull/84197
-
R. de C. Valle。“Rust 的 LLVM 控制流完整性 (CFI) 支持的跟踪问题 #89653。” GitHub。 https://github.com/rust-lang/rust/issues/89653。
-
“ControlFlowIntegrity。” The Rust Unstable Book。 https://doc.rust-lang.net.cn/unstable-book/compiler-flags/sanitizer.html#controlflowintegrity。
-
I. Lozano。“添加对 LLVM ShadowCallStack 的支持 #98208。” GitHub。 https://github.com/rust-lang/rust/pull/98208。
-
“ShadowCallStack。” The Rust Unstable Book。 https://doc.rust-lang.net.cn/unstable-book/compiler-flags/sanitizer.html#shadowcallstack。
-
W. Wiser。“添加对 LLVM SafeStack 的支持 #112000” GitHub。 https://github.com/rust-lang/rust/pull/112000
-
“SafeStack。” The Rust Unstable Book。 https://doc.rust-lang/org/unstable-book/compiler-flags/sanitizer.html#safestack。