Skip to content

Commit bc055a5

Browse files
joshlfCQ Bot Account
authored and
CQ Bot Account
committed
pw_kernel: Use MaybeUninit<u8> for stack memory
This solves two problems: - It ensures that values which are not entirely initialized (e.g. which contain padding bytes) can be written to the stack before the thread is spawned [1] - It ensures that pointers written to the stack retain provenance [2] [1] After a thread is spawned, the Rust Abstract Machine (AM) sees it as stack memory, and not as a Rust variable. At that point, this concern no longer applies. [2] rust-lang/unsafe-code-guidelines#286 (comment) Change-Id: I5eece2c6181f6dc793815208a2ad2b3fdbfa456c Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/287296 Docs-Not-Needed: Joshua Liebow-Feeser <[email protected]> Presubmit-Verified: CQ Bot Account <[email protected]> Lint: Lint 🤖 <[email protected]> Commit-Queue: Auto-Submit <[email protected]> Reviewed-by: Erik Gilling <[email protected]> Pigweed-Auto-Submit: Joshua Liebow-Feeser <[email protected]>
1 parent aa93459 commit bc055a5

File tree

6 files changed

+65
-22
lines changed

6 files changed

+65
-22
lines changed

pw_kernel/kernel/arch.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ mod host;
2727
#[cfg(feature = "arch_host")]
2828
pub use host::Arch;
2929

30+
use core::mem::MaybeUninit;
31+
3032
use crate::scheduler::{thread::Stack, SchedulerState};
3133
use crate::sync::spinlock::SpinLockGuard;
3234

@@ -68,7 +70,7 @@ pub trait ThreadState {
6870
fn initialize_user_frame(
6971
&mut self,
7072
kernel_stack: Stack,
71-
initial_sp: *mut u8,
73+
initial_sp: *mut MaybeUninit<u8>,
7274
initial_function: extern "C" fn(usize, usize),
7375
args: (usize, usize),
7476
);

pw_kernel/kernel/arch/arm_cortex_m/threads.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// the License.
1414

1515
use core::arch::asm;
16-
use core::mem;
16+
use core::mem::{self, MaybeUninit};
1717

1818
use cortex_m::peripheral::SCB;
1919

@@ -169,7 +169,7 @@ impl super::super::ThreadState for ArchThreadState {
169169
fn initialize_user_frame(
170170
&mut self,
171171
kernel_stack: Stack,
172-
initial_sp: *mut u8,
172+
initial_sp: *mut MaybeUninit<u8>,
173173
initial_function: extern "C" fn(usize, usize),
174174
args: (usize, usize),
175175
) {

pw_kernel/kernel/arch/host.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// License for the specific language governing permissions and limitations under
1313
// the License.
1414

15+
use core::mem::MaybeUninit;
16+
1517
use pw_log::info;
1618

1719
use crate::arch::ArchInterface;
@@ -48,7 +50,7 @@ impl super::ThreadState for ThreadState {
4850
fn initialize_user_frame(
4951
&mut self,
5052
_kernel_stack: Stack,
51-
_initial_sp: *mut u8,
53+
_initial_sp: *mut MaybeUninit<u8>,
5254
_initial_function: extern "C" fn(usize, usize),
5355
_args: (usize, usize),
5456
) {

pw_kernel/kernel/arch/riscv/threads.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::arch::{Arch, ArchInterface};
1616
use crate::scheduler::{self, thread::Stack, SchedulerState};
1717
use crate::sync::spinlock::SpinLockGuard;
1818
use core::arch::naked_asm;
19-
use core::mem;
19+
use core::mem::{self, MaybeUninit};
2020
use log_if::debug_if;
2121

2222
const LOG_CONTEXT_SWITCH: bool = false;
@@ -114,7 +114,7 @@ impl super::super::ThreadState for ArchThreadState {
114114
fn initialize_user_frame(
115115
&mut self,
116116
kernel_stack: Stack,
117-
initial_sp: *mut u8,
117+
initial_sp: *mut MaybeUninit<u8>,
118118
initial_function: extern "C" fn(usize, usize),
119119
args: (usize, usize),
120120
) {

pw_kernel/kernel/lib.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ pub use scheduler::{
3737
};
3838
pub use timer::{Clock, Duration};
3939

40+
// Used by the `init_thread!` macro.
41+
#[doc(hidden)]
42+
pub use scheduler::thread::{StackStorage, StackStorageExt};
43+
4044
#[no_mangle]
4145
#[allow(non_snake_case)]
4246
pub extern "C" fn pw_assert_HandleFailure() -> ! {
@@ -99,10 +103,11 @@ macro_rules! init_thread {
99103
info!("initializing thread: {}", $name as &'static str);
100104
thread.initialize_kernel_thread(
101105
{
102-
static mut STACK: [u8; $stack_size] = [0; $stack_size];
106+
static mut STACK_STORAGE: $crate::StackStorage<{ $stack_size }> =
107+
$crate::StackStorageExt::ZEROED;
103108
#[allow(static_mut_refs)]
104109
unsafe {
105-
Stack::from_slice(&STACK)
110+
Stack::from_slice(&STACK_STORAGE)
106111
}
107112
},
108113
$entry,
@@ -136,17 +141,19 @@ macro_rules! init_non_priv_thread {
136141
);
137142
thread.initialize_non_priv_thread(
138143
{
139-
static mut STACK: [u8; $stack_size] = [0; $stack_size];
144+
static mut STACK_STORAGE: $crate::StackStorage<{ $stack_size }> =
145+
$crate::StackStorageExt::ZEROED;
140146
#[allow(static_mut_refs)]
141147
unsafe {
142-
Stack::from_slice(&STACK)
148+
Stack::from_slice(&STACK_STORAGE)
143149
}
144150
},
145151
{
146-
static mut STACK: [u8; $stack_size] = [0; $stack_size];
152+
static mut STACK_STORAGE: $crate::StackStorage<{ $stack_size }> =
153+
$crate::StackStorageExt::ZEROED;
147154
#[allow(static_mut_refs)]
148155
unsafe {
149-
Stack::from_slice(&STACK)
156+
Stack::from_slice(&STACK_STORAGE)
150157
}
151158
},
152159
$entry,

pw_kernel/kernel/scheduler/thread.rs

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// the License.
1414

1515
use core::cell::UnsafeCell;
16+
use core::mem::MaybeUninit;
1617

1718
use list::*;
1819
use pw_log::info;
@@ -21,19 +22,46 @@ use crate::arch::ThreadState;
2122

2223
use super::SCHEDULER_STATE;
2324

25+
/// The memory backing a thread's stack before it has been started.
26+
///
27+
/// After a thread has been started, ownership of its stack's memory is (from
28+
/// the Rust Abstract Machine (AM) perspective) relinquished, and so the type we
29+
/// use to represent that memory is irrelevant.
30+
///
31+
/// However, while we are initializing a thread in preparation for starting it,
32+
/// we are operating on that memory as a normal Rust variable, and so its type
33+
/// is important.
34+
///
35+
/// Using `MaybeUninit<u8>` instead of `u8` is important for two reasons:
36+
/// - It ensures that it is sound to write values which are not entirely
37+
/// initialized (e.g., which contain padding bytes).
38+
/// - It ensures that pointers written to the stack [retain
39+
/// provenance][provenance].
40+
///
41+
/// [provenance]: https://github.com/rust-lang/unsafe-code-guidelines/issues/286#issuecomment-2837585644
42+
pub type StackStorage<const N: usize> = [MaybeUninit<u8>; N];
43+
44+
pub trait StackStorageExt {
45+
const ZEROED: Self;
46+
}
47+
48+
impl<const N: usize> StackStorageExt for StackStorage<N> {
49+
const ZEROED: StackStorage<N> = [MaybeUninit::new(0); N];
50+
}
51+
2452
#[derive(Clone, Copy)]
2553
pub struct Stack {
2654
// Starting (lowest) address of the stack. Inclusive.
27-
start: *const u8,
55+
start: *const MaybeUninit<u8>,
2856

2957
// Ending (highest) address of the stack. Exclusive.
30-
end: *const u8,
58+
end: *const MaybeUninit<u8>,
3159
}
3260

3361
#[allow(dead_code)]
3462
impl Stack {
35-
pub const fn from_slice(slice: &[u8]) -> Self {
36-
let start: *const u8 = slice.as_ptr();
63+
pub const fn from_slice(slice: &[MaybeUninit<u8>]) -> Self {
64+
let start: *const MaybeUninit<u8> = slice.as_ptr();
3765
// Safety: offset based on known size of slice.
3866
let end = unsafe { start.add(slice.len()) };
3967
Self { start, end }
@@ -46,23 +74,27 @@ impl Stack {
4674
}
4775
}
4876

49-
pub fn start(&self) -> *const u8 {
77+
pub fn start(&self) -> *const MaybeUninit<u8> {
5078
self.start
5179
}
52-
pub fn end(&self) -> *const u8 {
80+
pub fn end(&self) -> *const MaybeUninit<u8> {
5381
self.end
5482
}
5583

56-
pub fn initial_sp(&self, alignment: usize) -> *const u8 {
84+
pub fn initial_sp(&self, alignment: usize) -> *const MaybeUninit<u8> {
5785
// Use a zero sized allocation to align the initial stack pointer.
5886
Self::aligned_stack_allocation(self.end, 0, alignment)
5987
}
6088

61-
pub fn contains(&self, ptr: *const u8) -> bool {
89+
pub fn contains(&self, ptr: *const MaybeUninit<u8>) -> bool {
6290
ptr >= self.start && ptr < self.end
6391
}
6492

65-
pub fn aligned_stack_allocation(sp: *const u8, size: usize, alignment: usize) -> *const u8 {
93+
pub fn aligned_stack_allocation(
94+
sp: *const MaybeUninit<u8>,
95+
size: usize,
96+
alignment: usize,
97+
) -> *const MaybeUninit<u8> {
6698
let sp = sp.wrapping_byte_sub(size);
6799
let offset = sp.align_offset(alignment);
68100
if offset > 0 {
@@ -239,7 +271,7 @@ impl Thread {
239271
// the RISC-V calling convention. Ideally this would be
240272
// architecture dependant. However, this value will eventually
241273
// be passed in from user space.
242-
main_stack.initial_sp(16) as *mut u8,
274+
main_stack.initial_sp(16) as *mut MaybeUninit<u8>,
243275
Self::trampoline,
244276
args,
245277
);

0 commit comments

Comments
 (0)