How to do stack overflow protection in no_std?

According to no_std - The Embedded Rust Book (rust-lang.org)

stack overflow protection is not supported in no_std

I wonder how to implement it by hand:

  1. A no_std Rust program running in Linux Userspace
  2. A no_std Rust program running on an embedded board, run in bare metal environment.

The way it works normally is to have a guard page -- virtual memory that is mapped with no access permission -- and then stack probes that poke memory whenever the stack usage grows by a large amount. Then you get a page fault when that probe goes too far, into the guard page.

The probing part should be fine for no_std, but it would be up to you to arrange a mechanism like the guard page for your stack.

What do you mean by "no_std program" ? Typically running a program means defining how it interacts with the environment in some way; and that's the same way you would set up stack protection. Rust, out of the box, doesn't offer a way to compile a "no_std userspace Linux executable".

Minimum no_std program under linux
Require nightly compiler.
This program prints Hello World!
This compiles in Rust playground, but cannot be run because main not found.

You can compile it by rustc +nightly hello.rs -lc, then ./hello to run it

// hello.rs
#![feature(lang_items)]
#![feature(start)]

#![no_std]
use core::panic::PanicInfo;

#[panic_handler]
fn panic(_panic_info: &PanicInfo) -> ! {
    loop {}
}

#[lang = "eh_personality"] #[no_mangle] pub extern fn eh_personality() {}

extern "C" {
    fn write(fd: i32, buf: *const u8, count: usize) -> isize;
}

#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
    unsafe {
        write(1, b"Hello World!\n" as *const u8, 13);
    }
    0
}
1 Like

How to implement stack probe in Rust code?

Or is it Rust built-in, even in no_std environment?

It's built-in, yes, and should be on no_std too. Not all arches support it in LLVM though.

Embedded devices don't normally have any memory protection or use virtual memory manager, so you won't get a nice segfault like you do when running on an OS.

Depending on where you place the stack in physical memory, you can make sure a stack overflow starts writing over heap memory or global variables, or it could run past the end of RAM and trigger a bus fault (an interrupt you can catch and use to trigger a reboot).

Something else you can do is play with the linker script to set aside some memory immediately after the stack that can be used to detect when a stack overflow has occurred (the purple area in the image below).

image

Because it's not something injected by the compiler you would still need to check that memory manually every now and then, either explicitly or using a timer+interrupt. If you've heard of stack canaries, this is a very similar concept.

Personally, I'd prefer the bus fault version. Checking if your "stack canary" has been overwritten will add some performance overhead and complication, and the end result will be the same - your device will either reset (in production) or pause and wait for you to attach a debugger. It would also be compatible with stack probes because you get a bus fault when both reading and writing to physical memory that doesn't exist, whereas a canary only detects writes.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.