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() -> 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    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() -> usize {
45    unsafe { __get_unsafe_stack_ptr() }
46}
47
48pub fn clean_stack() {
49    // Stacks are 2Mo, clean 1Mo.
50    const CLEAN_SIZE: usize = 0x100000;
51    let page_size: usize = zx::system_get_page_size() as usize;
52    let stack_ptr_factories = [get_unsafe_stack_ptr, get_safe_stack_ptr];
53    for factory in stack_ptr_factories {
54        // Compute the pointers to the stack as a function call from this function to ensure the
55        // pointed memory is unused.
56        let v = factory();
57        // Afford some space on the stack for the op_range call.
58        let v = v - 64;
59        let max_addr = v - (v % page_size);
60        let start_addr = max_addr - CLEAN_SIZE;
61        let _ =
62            fuchsia_runtime::vmar_root_self().op_range(zx::VmarOp::ZERO, start_addr, CLEAN_SIZE);
63    }
64}
65
66#[cfg(test)]
67mod test {
68    use super::*;
69
70    #[inline(never)]
71    fn clean_stack_at_level(level: usize) -> usize {
72        if level == 0 {
73            clean_stack();
74            0
75        } else {
76            1 + clean_stack_at_level(level - 1)
77        }
78    }
79
80    #[::fuchsia::test]
81    fn test_clean_stack() {
82        let mut array = [0u8; 256];
83        for i in 0..256 {
84            array[i] = 255 - (i as u8);
85        }
86        for i in 0..4096 {
87            assert_eq!(clean_stack_at_level(i), i);
88        }
89        for i in 0..256 {
90            assert_eq!(array[i], 255 - (i as u8));
91        }
92    }
93}