Check stack overflow in main task (#4161)

* Check main task for stack overflow

* Reuse esp-hal's config
This commit is contained in:
Dániel Buga 2025-09-22 12:01:09 +02:00 committed by GitHub
parent 0ede07d697
commit 6ee143eef9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 39 additions and 13 deletions

View File

@ -15,5 +15,10 @@ fn main() -> Result<(), Box<dyn Error>> {
.expect("Failed to read esp_config.yml for esp-preempt");
generate_config_from_yaml_definition(&cfg_yaml, true, true, Some(chip)).unwrap();
// Emit the default stack guard value if not set by the user.
if std::env::var("ESP_HAL_CONFIG_STACK_GUARD_VALUE").is_err() {
println!("cargo:rustc-env=ESP_HAL_CONFIG_STACK_GUARD_VALUE=3740121773");
}
Ok(())
}

View File

@ -8,3 +8,9 @@ options:
constraints:
- type:
validator: positive_integer
- name: stack-guard-value
description: The stack overflow guard value. This must be the same value as esp_hal's stack guard value.
default:
- value: 3740121773
display_hint: Hex

View File

@ -32,6 +32,8 @@ mod timer;
mod timer_queue;
mod wait_queue;
use core::mem::MaybeUninit;
pub(crate) use esp_alloc::InternalMemory;
use esp_hal::{
Blocking,
@ -121,8 +123,20 @@ pub fn start(timer: impl TimerSource) {
SCHEDULER.with(move |scheduler| {
scheduler.setup(TimeDriver::new(timer.timer()));
// allocate the default tasks
task::allocate_main_task(scheduler);
// Allocate the default task. The stack bottom is set to the stack guard's address as the
// rest of the stack is unusable anyway.
unsafe extern "C" {
static _stack_start_cpu0: u32;
static __stack_chk_guard: u32;
}
let stack_top = &raw const _stack_start_cpu0;
let stack_bottom = (&raw const __stack_chk_guard).cast::<MaybeUninit<u32>>();
let stack_slice = core::ptr::slice_from_raw_parts_mut(
stack_bottom.cast_mut(),
stack_top as usize - stack_bottom as usize,
);
task::allocate_main_task(scheduler, stack_slice);
task::setup_multitasking();

View File

@ -201,7 +201,7 @@ pub(crate) struct Task {
pub cpu_context: CpuContext,
pub thread_semaphore: Option<SemaphorePtr>,
pub state: TaskState,
pub _allocated_stack: *mut [MaybeUninit<u32>],
pub stack: *mut [MaybeUninit<u32>],
pub priority: usize,
pub wakeup_at: u64,
@ -226,7 +226,8 @@ pub(crate) struct Task {
pub(crate) heap_allocated: bool,
}
const STACK_CANARY: u32 = 0xDEEDBAAD;
const STACK_CANARY: u32 =
const { esp_config::esp_config_int!(u32, "ESP_HAL_CONFIG_STACK_GUARD_VALUE") };
impl Task {
pub(crate) fn new(
@ -251,7 +252,7 @@ impl Task {
cpu_context: new_task_context(task_fn, param, stack_top),
thread_semaphore: None,
state: TaskState::Ready,
_allocated_stack: stack,
stack,
current_queue: None,
priority,
@ -267,15 +268,10 @@ impl Task {
}
pub(crate) fn ensure_no_stack_overflow(&self) {
// TODO: fix this for main task
if self._allocated_stack.is_empty() {
return;
}
assert_eq!(
// This cast is safe to do from MaybeUninit<u32> because this is the word we've written
// during initialization.
unsafe { self._allocated_stack.cast::<u32>().read() },
unsafe { self.stack.cast::<u32>().read() },
STACK_CANARY,
"Stack overflow detected in {:?}",
self as *const Task
@ -300,7 +296,7 @@ pub(crate) static mut MAIN_TASK: Task = Task {
cpu_context: CpuContext::new(),
thread_semaphore: None,
state: TaskState::Ready,
_allocated_stack: core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0),
stack: core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0),
current_queue: None,
priority: 0,
@ -314,16 +310,21 @@ pub(crate) static mut MAIN_TASK: Task = Task {
heap_allocated: false,
};
pub(super) fn allocate_main_task(scheduler: &mut SchedulerState) {
pub(super) fn allocate_main_task(scheduler: &mut SchedulerState, stack: *mut [MaybeUninit<u32>]) {
let mut main_task_ptr = unwrap!(NonNull::new(&raw mut MAIN_TASK));
debug!("Main task created: {:?}", main_task_ptr);
unsafe {
stack
.cast::<MaybeUninit<u32>>()
.write(MaybeUninit::new(STACK_CANARY));
let main_task = main_task_ptr.as_mut();
// Reset main task properties. The rest should be cleared when the task is deleted.
main_task.priority = 0;
main_task.state = TaskState::Ready;
main_task.stack = stack;
}
debug_assert!(