starnix_stack/
lib.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use zx;
6
7unsafe extern "C" {
8    fn __get_unsafe_stack_ptr() -> usize;
9}
10
11/// Compute the safe stack pointer inside this function.
12///
13/// This must never be inlined to ensure the stack pointer is not the one for the previous
14/// function.
15#[inline(never)]
16fn get_safe_stack_ptr() -> Option<usize> {
17    let mut sp: usize;
18    unsafe {
19        #[cfg(target_arch = "x86_64")]
20        std::arch::asm!(
21            "mov {0}, rsp",
22            out(reg) sp,
23            options(nomem)
24        );
25        #[cfg(target_arch = "aarch64")]
26        std::arch::asm!(
27            "mov {0}, sp",
28            out(reg) sp,
29            options(nomem)
30        );
31        #[cfg(target_arch = "riscv64")]
32        std::arch::asm!(
33            "mv {0}, sp",
34            out(reg) sp,
35            options(nomem)
36        );
37    }
38    Some(sp)
39}
40
41/// Compute the safe stack pointer inside this function.
42///
43/// This can be inlined because it calls into C, so the underlying call will never be inlined.
44fn get_unsafe_stack_ptr() -> Option<usize> {
45    // Only x86_64 uses the unsafe stack. Other architecture use the shadow call stack that
46    // doesn't need to be cleaned up.
47    if cfg!(target_arch = "x86_64") { unsafe { Some(__get_unsafe_stack_ptr()) } } else { None }
48}
49
50pub fn clean_stack() {
51    // Stacks are 2Mo, clean 1Mo.
52    const CLEAN_SIZE: usize = 0x100000;
53    let page_size: usize = zx::system_get_page_size() as usize;
54    let stack_ptr_factories = [get_unsafe_stack_ptr, get_safe_stack_ptr];
55    for factory in stack_ptr_factories {
56        // Compute the pointers to the stack as a function call from this function to ensure the
57        // pointed memory is unused.
58        let Some(v) = factory() else {
59            continue;
60        };
61        // Afford some space on the stack for the op_range call.
62        let v = v - 64;
63        let max_addr = v - (v % page_size);
64        let start_addr = max_addr - CLEAN_SIZE;
65        let _ =
66            fuchsia_runtime::vmar_root_self().op_range(zx::VmarOp::ZERO, start_addr, CLEAN_SIZE);
67    }
68}
69
70#[cfg(test)]
71mod test {
72    use super::*;
73
74    #[inline(never)]
75    fn clean_stack_at_level(level: usize) -> usize {
76        if level == 0 {
77            clean_stack();
78            0
79        } else {
80            1 + clean_stack_at_level(level - 1)
81        }
82    }
83
84    #[::fuchsia::test]
85    fn test_clean_stack() {
86        let mut array = [0u8; 256];
87        for i in 0..256 {
88            array[i] = 255 - (i as u8);
89        }
90        for i in 0..4096 {
91            assert_eq!(clean_stack_at_level(i), i);
92        }
93        for i in 0..256 {
94            assert_eq!(array[i], 255 - (i as u8));
95        }
96    }
97}