#memory-layout #stack-overflow #stack-memory #memory-protection #cortex-m #arm #stack-protection

app flip-link

通过knurling-team将嵌入式程序的内存布局翻转以防止栈溢出

10个版本

0.1.9 2024年8月14日
0.1.8 2024年3月6日
0.1.7 2023年7月20日
0.1.6 2022年3月24日
0.1.2 2020年11月26日

#24 in 嵌入式开发

Download history 1101/week @ 2024-05-02 1310/week @ 2024-05-09 1705/week @ 2024-05-16 1806/week @ 2024-05-23 2290/week @ 2024-05-30 1826/week @ 2024-06-06 1382/week @ 2024-06-13 1670/week @ 2024-06-20 1796/week @ 2024-06-27 2278/week @ 2024-07-04 1846/week @ 2024-07-11 1363/week @ 2024-07-18 1253/week @ 2024-07-25 1426/week @ 2024-08-01 1752/week @ 2024-08-08 1954/week @ 2024-08-15

每月下载量 6,589次

MIT/Apache

58KB
516 代码行

flip-link

为您的嵌入式程序添加零成本的栈溢出保护

问题

裸金属Rust程序在存在栈溢出的情况下可能 是内存安全的。例如,基于v0.6.x版本的 cortex-m-rt 包的Rust程序就是这样。

以下程序,其中不包含 unsafe 代码块,如果达到栈溢出条件,可能会遇到 未定义的行为

// static variables placed in the .bss / .data sections
static FLAG1: AtomicBool = AtomicU32::new(false); // .bss
static FLAG2: AtomicBool = AtomicU32::new(true);  // .data

fn main() {
    let _x = fib(100);
}

#[inline(never)]
fn fib(n: u32) -> u32 {
    // allocate and initialize 4 kilobytes of stack memory
    let _use_stack = [0xAA; 1024];

    if n < 2 {
        1
    } else {
        fib(n - 1) + fib(n - 2) // recursion
    }
}

#[interrupt]
fn interrupt_handler() {
    // does some operation with `FLAG1` and `FLAG2`
}

以下是在RAM中ARM Cortex-M程序默认内存布局的示例。

left: default memory layout of ARM Cortex-M programs; right: stack overflow condition

函数调用栈,也称为“栈”,在函数调用和创建局部变量(例如 let x)时向下增长(这些变量也放在栈上)。

如果栈增长过大,则会与包含所有程序静态变量的 .bss + .data 区域冲突。这种冲突会导致静态变量被无关数据覆盖。这可能导致程序观察到静态变量处于无效状态:例如一个 AtomicBool 可能保持 3 的值 -- 这是因为Rust ABI期望这个单字节变量为 01,这是未定义的行为。

解决方案

一个可能的解决方案是更改程序的内存布局,并将栈放置在 .bss+.data 区域 下方

使用这种反转的内存布局(如图所示),堆栈不能与静态变量冲突。相反,它将与物理RAM内存区域的边界冲突。在ARM Cortex-M架构中,尝试读取或写入RAM区域的边界将产生“硬件异常”。cortex-m-rtcrate提供了一个API来处理这种条件:可以定义一个HardFault异常处理程序;当尝试执行无效的内存操作时,将执行此“处理程序”(函数)。

left: flipped memory layout; right: stack overflow condition

flip-link实现了这种堆栈溢出解决方案。将您的程序与flip-link链接会产生反转的内存布局,在堆栈溢出情况下内存安全。

架构支持

flip-link已知可以与链接到0.6.x版本的cortex-m-rtcrate的ARM Cortex-M程序一起使用,并使用Rust工具链(LLD)提供的链接器进行链接。目前,它尚未与其他架构或运行时crate进行测试。

安装

flip-link可在crates.io获取。要安装它,运行

$ cargo install flip-link

使用

.cargo/config.toml中将链接器从rust-lld(默认)更改为flip-link

[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# (..)
linker = "flip-link"

在Cargo < 1.74版本中,使用rustflags更改链接器

[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# (..)
rustflags = [
  "-C", "linker=flip-link", # <- add this
  # (..)
]

请注意,如果您使用GNU ld或GNU gcc链接程序,则此方法不会起作用。其他链接器的支持正在问题#1中跟踪。

测试

我们的CI执行各种检查。您可以在本地运行它们以确保您的PR将通过CI

  • cargofmt --all ----check
  • cargoclippy ----deny warnings
  • cargoxtest
    • 这将安装当前版本的flip-link并运行cargo test

支持

flip-linkKnurling项目的一部分,是Ferrous Systems改进用于开发嵌入式系统工具的努力。

如果您认为我们的工作很有用,请考虑通过GitHub Sponsors进行赞助。

许可证

根据您的选择,许可为以下之一

贡献

除非您明确说明,否则根据Apache-2.0许可证定义的任何旨在包含在您的工作中的故意提交的贡献,均应按上述方式许可,而不附加任何其他条款或条件。

依赖项

~2.5MB
~45K SLoC