From 693e28645521cea78143e67892e665e33b97dd35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:56:02 +0000 Subject: [PATCH 1/2] Initial plan From a090beb8d108ed4c01b06296388a6c4af16f59ec Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 4 Apr 2026 15:15:57 +0000 Subject: [PATCH 2/2] Fix four overflow/underflow bugs exposed by tests Agent-Logs-Url: https://github.com/OsirisRTOS/osiris/sessions/f63a1358-2cfb-4d9d-8ada-ddd2c84eff6f Co-authored-by: xarantolus <32465636+xarantolus@users.noreply.github.com> --- src/mem.rs | 21 +++++++++++++++++++++ src/mem/pfa/bitset.rs | 15 ++++++++++----- src/sched/thread.rs | 2 +- src/types/array.rs | 8 ++++---- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/mem.rs b/src/mem.rs index dc1bcdf..4420e1e 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -92,3 +92,24 @@ pub fn align_up(size: usize) -> usize { let align = align_of::(); (size + align - 1) & !(align - 1) } + +/// Initialize the global allocator with a small heap backed by host memory. +/// Safe to call multiple times; initialization only runs once. +#[cfg(test)] +pub(crate) fn init_test_heap() { + use hal::mem::PhysAddr; + + static INIT: std::sync::Once = std::sync::Once::new(); + INIT.call_once(|| { + let len = 65536usize; + let layout = + std::alloc::Layout::from_size_align(len, core::mem::align_of::()).unwrap(); + let ptr = unsafe { std::alloc::alloc(layout) }; + assert!(!ptr.is_null(), "Failed to allocate test heap memory"); + let start = ptr as usize; + let end = start.checked_add(len).expect("test heap address overflows"); + let range = PhysAddr::new(start)..PhysAddr::new(end); + let mut allocator = GLOBAL_ALLOCATOR.lock(); + unsafe { allocator.add_range(&range).unwrap() }; + }); +} diff --git a/src/mem/pfa/bitset.rs b/src/mem/pfa/bitset.rs index 16137e6..14aa274 100644 --- a/src/mem/pfa/bitset.rs +++ b/src/mem/pfa/bitset.rs @@ -118,35 +118,40 @@ impl super::Allocator for Allocator { if len >= page_count { // Mark the allocated pages as used. let mut idx = start / Self::BITS_PER_WORD; + let mut count = page_count; // Mark all bits in the first word as used. { let skip = start % Self::BITS_PER_WORD; - let rem = len.min(Self::BITS_PER_WORD) - skip; + let rem = (Self::BITS_PER_WORD - skip).min(count); self.l1[idx] &= !((!0usize).unbounded_shl((Self::BITS_PER_WORD - rem) as u32) >> skip); - if len <= rem { + if count <= rem { return Some(self.begin + (start * super::PAGE_SIZE)); } - len -= rem; + count -= rem; idx += 1; } // Mark all bits in the middle words as used. { - let mid_cnt = len / Self::BITS_PER_WORD; + let mid_cnt = count / Self::BITS_PER_WORD; for i in 0..mid_cnt { self.l1[idx + i] = 0; } idx += mid_cnt; + count -= mid_cnt * Self::BITS_PER_WORD; } // Mark the remaining bits in the last word as used. - self.l1[idx] &= !((!0usize).unbounded_shl((Self::BITS_PER_WORD - (len % Self::BITS_PER_WORD)) as u32)); + if count > 0 { + debug_assert!(idx < N, "bit index out of bounds in last-word marking"); + self.l1[idx] &= !((!0usize).unbounded_shl((Self::BITS_PER_WORD - count) as u32)); + } return Some(self.begin + (start * super::PAGE_SIZE)); } } diff --git a/src/sched/thread.rs b/src/sched/thread.rs index b5ad71f..8207f02 100644 --- a/src/sched/thread.rs +++ b/src/sched/thread.rs @@ -187,7 +187,7 @@ impl RtServer { pub fn replenish(&mut self) { self.deadline = self.deadline + self.period as u64; - self.budget_left += self.budget; + self.budget_left = self.budget_left.saturating_add(self.budget); } pub fn consume(&mut self, dt: u64) -> Option { diff --git a/src/types/array.rs b/src/types/array.rs index 1f08e56..082db64 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -94,7 +94,7 @@ impl IndexMap /// /// Returns an iterator over the elements in the map. pub fn iter_from_cycle(&self, idx: Option<&K>) -> impl Iterator> { - self.data.iter().cycle().skip(K::to_index(idx) + 1) + self.data.iter().cycle().skip(K::to_index(idx).wrapping_add(1) % N) } /// Get the next index that contains a value (this will cycle). @@ -106,7 +106,7 @@ impl IndexMap for (i, elem) in self.iter_from_cycle(idx).enumerate() { if elem.is_some() { let idx = K::to_index(idx); - return Some((idx + i + 1) % N); + return Some((idx.wrapping_add(i).wrapping_add(1)) % N); } } @@ -669,14 +669,14 @@ mod tests { #[test] fn reserve_underflow() { // N=8, fill 7 elements so len=7 and extra.len()=0. - // reserve(2): 7+2=9 > 8+0=8, needs grow. grow = 2 - 8 + 0 = underflow. + // reserve(2): 7+2=9 > 8+0=8, needs grow. grow = self.len + additional - N = 7+2-8 = 1. + crate::mem::init_test_heap(); let mut vec = Vec::::new(); for i in 0..7usize { vec.push(i).unwrap(); } assert_eq!(vec.len(), 7); - // FIXME: Gives OOM error vec.reserve(2).unwrap(); }