Skip to content

Commit

Permalink
monitor: report potential stack overflows
Browse files Browse the repository at this point in the history
Still not 100% ideal as sometimes stack overflows occur multiple
pages out-of-bounds of the stack region, but this should be able
to catch some of them and save time debugging.

Signed-off-by: Ivan-Velickovic <[email protected]>
  • Loading branch information
Ivan-Velickovic committed Jan 6, 2025
1 parent 1d36453 commit 8b98969
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 19 deletions.
9 changes: 9 additions & 0 deletions monitor/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ seL4_Word tcbs[MAX_TCBS];
seL4_Word scheduling_contexts[MAX_TCBS];
seL4_Word notification_caps[MAX_TCBS];

/* For reporting potential stack overflows, keep track of the stack regions for each PD. */
seL4_Word pd_stack_addrs[MAX_PDS];

struct region {
uintptr_t paddr;
uintptr_t size_bits;
Expand Down Expand Up @@ -916,6 +919,12 @@ static void monitor(void)
#error "Unknown architecture to print a VM fault for"
#endif

seL4_Word fault_addr = seL4_GetMR(seL4_VMFault_Addr);
seL4_Word stack_addr = pd_stack_addrs[badge];
if (fault_addr < stack_addr && fault_addr >= stack_addr - 0x1000) {
puts("MON|ERROR: potential stack overflow, fault address within one page outside of stack region\n");
}

break;
}
default:
Expand Down
30 changes: 11 additions & 19 deletions tool/microkit/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use std::mem::size_of;
use std::path::{Path, PathBuf};
use util::{
bytes_to_struct, comma_sep_u64, comma_sep_usize, json_str, json_str_as_bool, json_str_as_u64,
struct_to_bytes,
monitor_serialise_u64_vec, struct_to_bytes,
};

// Corresponds to the IPC buffer symbol in libmicrokit and the monitor
Expand Down Expand Up @@ -453,6 +453,7 @@ struct BuiltSystem {
ntfn_caps: Vec<u64>,
pd_elf_regions: Vec<Vec<Region>>,
pd_setvar_values: Vec<Vec<u64>>,
pd_stack_addrs: Vec<u64>,
kernel_objects: Vec<Object>,
initial_task_virt_region: MemoryRegion,
initial_task_phys_region: MemoryRegion,
Expand Down Expand Up @@ -1362,6 +1363,7 @@ fn build_system(
// Here we create a memory region/mapping for the stack for each PD.
// We allocate the stack at the highest possible virtual address that the
// kernel allows us.
let mut pd_stack_addrs = Vec::with_capacity(system.protection_domains.len());
for pd in &system.protection_domains {
let stack_mr = SysMemoryRegion {
name: format!("STACK:{}", pd.name),
Expand All @@ -1380,6 +1382,8 @@ fn build_system(
text_pos: None,
};

pd_stack_addrs.push(stack_map.vaddr);

extra_mrs.push(stack_mr);
pd_extra_maps.get_mut(pd).unwrap().push(stack_map);
}
Expand Down Expand Up @@ -2865,6 +2869,7 @@ fn build_system(
ntfn_caps: notification_caps,
pd_elf_regions,
pd_setvar_values,
pd_stack_addrs,
kernel_objects,
initial_task_phys_region,
initial_task_virt_region,
Expand Down Expand Up @@ -3529,30 +3534,17 @@ fn main() -> Result<(), String> {
&bootstrap_invocation_data,
)?;

let mut tcb_cap_bytes = vec![0; (1 + built_system.tcb_caps.len()) * 8];
for (i, cap) in built_system.tcb_caps.iter().enumerate() {
let start = (i + 1) * 8;
let end = start + 8;
tcb_cap_bytes[start..end].copy_from_slice(&cap.to_le_bytes());
}
let mut sched_cap_bytes = vec![0; (1 + built_system.sched_caps.len()) * 8];
for (i, cap) in built_system.sched_caps.iter().enumerate() {
let start = (i + 1) * 8;
let end = start + 8;
sched_cap_bytes[start..end].copy_from_slice(&cap.to_le_bytes());
}
let mut ntfn_cap_bytes = vec![0; (1 + built_system.ntfn_caps.len()) * 8];
for (i, cap) in built_system.ntfn_caps.iter().enumerate() {
let start = (i + 1) * 8;
let end = start + 8;
ntfn_cap_bytes[start..end].copy_from_slice(&cap.to_le_bytes());
}
let tcb_cap_bytes = monitor_serialise_u64_vec(&built_system.tcb_caps);
let sched_cap_bytes = monitor_serialise_u64_vec(&built_system.sched_caps);
let ntfn_cap_bytes = monitor_serialise_u64_vec(&built_system.ntfn_caps);
let pd_stack_addrs_bytes = monitor_serialise_u64_vec(&built_system.pd_stack_addrs);

monitor_elf.write_symbol("fault_ep", &built_system.fault_ep_cap_address.to_le_bytes())?;
monitor_elf.write_symbol("reply", &built_system.reply_cap_address.to_le_bytes())?;
monitor_elf.write_symbol("tcbs", &tcb_cap_bytes)?;
monitor_elf.write_symbol("scheduling_contexts", &sched_cap_bytes)?;
monitor_elf.write_symbol("notification_caps", &ntfn_cap_bytes)?;
monitor_elf.write_symbol("pd_stack_addrs", &pd_stack_addrs_bytes)?;
// We do MAX_PDS + 1 due to the index that the monitor uses (the badge) starting at 1.
let mut pd_names_bytes = vec![0; (MAX_PDS + 1) * PD_MAX_NAME_LENGTH];
for (i, pd) in system.protection_domains.iter().enumerate() {
Expand Down
13 changes: 13 additions & 0 deletions tool/microkit/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,19 @@ pub unsafe fn bytes_to_struct<T>(bytes: &[u8]) -> &T {
&body[0]
}

/// Serialise an array of u64 to a Vector of bytes. Pads the Vector of bytes
/// such that the first entry is empty.
pub fn monitor_serialise_u64_vec(vec: &[u64]) -> Vec<u8> {
let mut bytes = vec![0; (1 + vec.len()) * 8];
for (i, value) in vec.iter().enumerate() {
let start = (i + 1) * 8;
let end = start + 8;
bytes[start..end].copy_from_slice(&value.to_le_bytes());
}

bytes
}

#[cfg(test)]
mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope.
Expand Down

0 comments on commit 8b98969

Please sign in to comment.