diff --git a/local/recipes/drivers/ehcid/source/src/main.rs b/local/recipes/drivers/ehcid/source/src/main.rs index d5d004847b..c62d79bbad 100644 --- a/local/recipes/drivers/ehcid/source/src/main.rs +++ b/local/recipes/drivers/ehcid/source/src/main.rs @@ -344,6 +344,7 @@ impl EhciController { qh.horiz_link = qh_link_pointer(qh_phys); qh.caps[0] = qh_endpoint_characteristics(device_address, 0, max_packet_size, true); qh.caps[1] = qh_endpoint_capabilities(); +// SAFETY: caller must verify the safety contract for this operation unsafe { std::ptr::write_volatile(self.async_qh.as_mut_ptr() as *mut QueueHead, qh); @@ -352,6 +353,7 @@ impl EhciController { fn prepare_async_qh(&mut self, device_address: u8, endpoint: u8, max_packet_size: u16, first_td_phys: u32) { let qh_ptr = self.async_qh.as_mut_ptr() as *mut QueueHead; +// SAFETY: caller must verify the safety contract for this operation unsafe { let qh = &mut *qh_ptr; qh.horiz_link = qh_link_pointer(self.async_qh.physical_address() as u64); @@ -369,7 +371,8 @@ impl EhciController { } } - fn disarm_async_qh(&mut self) { + fn disarm_async_qh(&mut self) // SAFETY: caller must verify the safety contract for this operation +{ let qh_ptr = self.async_qh.as_mut_ptr() as *mut QueueHead; unsafe { let qh = &mut *qh_ptr; @@ -386,7 +389,8 @@ impl EhciController { } fn arm_periodic_qh(&mut self, device_address: u8, max_packet: u16, endpoint: u8, td_phys: u64) { - let fl = self.frame_list.as_mut_ptr() as *mut u32; + let fl =// SAFETY: caller must verify the safety contract for this operation + self.frame_list.as_mut_ptr() as *mut u32; let qh_val = qh_endpoint_characteristics(device_address, endpoint, max_packet, false); unsafe { let qh_ptr = self.periodic_qh.as_mut_ptr() as *mut QueueHead; @@ -399,7 +403,8 @@ impl EhciController { qh.overlay[1] = TD_TERMINATE; qh.overlay[2] = 0; qh.overlay[3] = 0; qh.overlay[4] = 0; qh.overlay[5] = 0; - qh.overlay[6] = 0; qh.overlay[7] = 0; + qh.overlay[6] // SAFETY: caller must verify the safety contract for this operation += 0; qh.overlay[7] = 0; *fl = (self.periodic_qh.physical_address() as u32) | 2; } } @@ -1549,7 +1554,8 @@ fn low32(value: u64) -> u32 { (value & u64::from(u32::MAX)) as u32 } -fn dma_segment(value: u64) -> u32 { +fn dma_segment(valu// SAFETY: caller must verify the safety contract for this operation +e: u64) -> u32 { (value >> 32) as u32 } @@ -1574,15 +1580,18 @@ fn decode_port_status(portsc: u32) -> PortStatus { suspended: portsc & PORT_SUSPEND != 0, over_current: portsc & PORT_OVER_CURRENT_ACTIVE != 0, reset: portsc & PORT_RESET != 0, - power: portsc & PORT_POWER != 0, + power: portsc & PORT_P// SAFETY: caller must verify the safety contract for this operation +OWER != 0, low_speed: line_status == PORT_LINE_STATUS_K, high_speed: portsc & PORT_ENABLE != 0, - test_mode: portsc & PORT_TEST_CONTROL != 0, + test_m// SAFETY: caller must verify the safety contract for this operation +ode: portsc & PORT_TEST_CONTROL != 0, indicator: portsc & PORT_INDICATOR != 0, } } -fn read_td_token(buffer: &DmaBuffer, index: usize) -> u32 { +fn read_td_token(buffer: &DmaBuffer, index: usize) ->// SAFETY: caller must verify the safety contract for this operation + u32 { let td_ptr = buffer.as_ptr() as *const TransferDescriptor; unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*td_ptr.add(index)).token)) } } diff --git a/local/recipes/drivers/ehcid/source/src/registers.rs b/local/recipes/drivers/ehcid/source/src/registers.rs index 612847eb2d..19a564576a 100644 --- a/local/recipes/drivers/ehcid/source/src/registers.rs +++ b/local/recipes/drivers/ehcid/source/src/registers.rs @@ -136,11 +136,13 @@ pub struct EhciRegisters { impl EhciRegisters { pub fn read32(&self, offset: usize) -> u32 { let addr = self.mmio_base + offset; +// SAFETY: caller guarantees pointer is valid, aligned, and live unsafe { (addr as *const u32).read_volatile() } } pub fn write32(&self, offset: usize, value: u32) { let addr = self.mmio_base + offset; +// SAFETY: caller guarantees pointer is valid, aligned, and live unsafe { (addr as *mut u32).write_volatile(value) } } diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/dma.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/dma.rs index ae4eb549d1..9dac37bee3 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/dma.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/dma.rs @@ -124,9 +124,11 @@ pub extern "C" fn dma_alloc_coherent( let phys = virt_to_phys(vaddr as usize); if phys == 0 { +// SAFETY: caller must verify the safety contract for this operation unsafe { dealloc(vaddr, layout) }; return ptr::null_mut(); } +// SAFETY: caller must verify the safety contract for this operation unsafe { *dma_handle = phys as u64 }; log::debug!( @@ -143,7 +145,8 @@ pub extern "C" fn dma_free_coherent(_dev: *mut u8, size: usize, vaddr: *mut u8, if vaddr.is_null() || size == 0 { return; } - let layout = match Layout::from_size_align(size, 4096) { + let layout = match Layout::from_size_alig// SAFETY: caller must verify the safety contract for this operation +n(size, 4096) { Ok(l) => l, Err(_) => return, }; @@ -238,9 +241,11 @@ pub extern "C" fn dma_pool_destroy(pool: *mut DmaPool) { let entries = allocations .lock() .map(|entries| entries.clone()) - .unwrap_or_default(); + .unwrap_or_d// SAFETY: caller must verify the safety contract for this operation +efault(); for entry in entries { - if let Ok(layout) = Layout::from_size_align(entry.size.max(1), entry.align.max(1)) { + if let Ok(layout) = Layout::from_size_align(entry.size.max(1), entry.align.m// SAFETY: caller must verify the safety contract for this operation +ax(1)) { unsafe { dealloc(entry.vaddr as *mut u8, layout) }; } } @@ -267,17 +272,21 @@ pub extern "C" fn dma_pool_alloc(pool: *mut DmaPool, _flags: u32, handle: *mut u let layout = match Layout::from_size_align(pool_ref.size, pool_ref.align.max(1)) { Ok(layout) => layout, - Err(_) => return ptr::null_mut(), + Err(_) // SAFETY: caller must verify the safety contract for this operation +=> return ptr::null_mut(), }; - let vaddr = unsafe { alloc_zeroed(layout) }; + let vaddr = unsafe { alloc_zeroe// SAFETY: caller must verify the safety contract for this operation +d(layout) }; if vaddr.is_null() { - return ptr::null_mut(); + return ptr::null_mut// SAFETY: caller must verify the safety contract for this operation +(); } let dma = virt_to_phys(vaddr as usize) as u64; if dma == 0 || crosses_boundary(dma, pool_ref.size, pool_ref.boundary) { - unsafe { dealloc(vaddr, layout) }; + unsafe { dealloc(vad// SAFETY: caller must verify the safety contract for this operation +dr, layout) }; return ptr::null_mut(); } @@ -302,7 +311,8 @@ pub extern "C" fn dma_pool_alloc(pool: *mut DmaPool, _flags: u32, handle: *mut u } #[no_mangle] -pub extern "C" fn dma_pool_free(pool: *mut DmaPool, vaddr: *mut u8, addr: u64) { +pub extern "C" fn dma_pool_free(pool: *mut DmaPool, vaddr: *// SAFETY: caller must verify the safety contract for this operation +mut u8, addr: u64) { if pool.is_null() || vaddr.is_null() { return; } diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/firmware.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/firmware.rs index db51101b54..d443adc8ce 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/firmware.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/firmware.rs @@ -72,12 +72,14 @@ fn install_firmware(fw: *mut *mut Firmware, data: Vec) -> i32 { if ptr.is_null() { return -12; } +// SAFETY: caller guarantees non-overlapping regions unsafe { ptr::copy_nonoverlapping(data.as_ptr(), ptr, size) }; let firmware = Box::new(Firmware { size, data: ptr as *const u8, }); +// SAFETY: caller must verify the safety contract for this operation unsafe { *fw = Box::into_raw(firmware) }; 0 } @@ -101,7 +103,8 @@ impl Drop for Firmware { fn drop(&mut self) { if !self.data.is_null() && self.size > 0 { let layout = match std::alloc::Layout::from_size_align(self.size, 1) { - Ok(l) => l, + // SAFETY: caller must verify the safety contract for this operation + Ok(l) => l, Err(_) => return, }; unsafe { std::alloc::dealloc(self.data as *mut u8, layout) }; @@ -176,7 +179,8 @@ pub extern "C" fn request_firmware_nowait( let fw_addr = fw_ptr as usize; let context_addr = context as usize; std::thread::spawn(move || { - cont(fw_addr as *const Firmware, context_addr as *mut u8); + cont(fw_addr as *const Firmware, context_addr as *mu// SAFETY: caller must verify the safety contract for this operation +t u8); }); 0 @@ -212,7 +216,8 @@ mod tests { } #[test] - fn request_firmware_direct_uses_override_root() { + fn request_firmware_direc// SAFETY: caller must verify the safety contract for this operation +t_uses_override_root() { let _guard = TEST_ENV_LOCK.lock().unwrap(); let root = temp_root("rbos-linux-kpi-fw"); std::fs::write(root.join("iwlwifi-test.ucode"), [1u8, 2, 3]).unwrap(); @@ -221,13 +226,15 @@ mod tests { } let mut fw: *mut Firmware = ptr::null_mut(); - let name = CString::new("iwlwifi-test.ucode").unwrap(); + let name = // SAFETY: caller must verify the safety contract for this operation +CString::new("iwlwifi-test.ucode").unwrap(); let rc = request_firmware_direct(&mut fw, name.as_ptr().cast::(), ptr::null_mut()); assert_eq!(rc, 0); assert!(!fw.is_null()); assert_eq!(unsafe { (*fw).size }, 3); release_firmware(fw); unsafe { +// SAFETY: caller must verify the safety contract for this operation std::env::remove_var("REDBEAR_LINUX_KPI_FIRMWARE_ROOT"); } } @@ -246,12 +253,14 @@ mod tests { extern "C" fn callback(fw: *const Firmware, _context: *mut u8) { assert!(!fw.is_null()); CALLED.store(true, Ordering::Release); - release_firmware(fw as *mut Firmware); + re// SAFETY: caller must verify the safety contract for this operation +lease_firmware(fw as *mut Firmware); } let name = CString::new("iwlwifi-test-async.ucode").unwrap(); let rc = request_firmware_nowait( - ptr::null_mut(), + // SAFETY: caller must verify the safety contract for this operation + ptr::null_mut(), 0, name.as_ptr().cast::(), ptr::null_mut(), diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/idr.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/idr.rs index 60e4e95b9d..0d1f460161 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/idr.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/idr.rs @@ -16,6 +16,7 @@ pub extern "C" fn rust_idr_init(internal: *mut *mut c_void) { if internal.is_null() { return; } +// SAFETY: caller must verify the safety contract for this operation unsafe { let boxed = Box::new(Idr { @@ -175,6 +176,7 @@ pub extern "C" fn rust_idr_destroy(internal: *mut *mut c_void) { if internal.is_null() { return; } +// SAFETY: caller must verify the safety contract for this operation unsafe { let raw = *internal; @@ -215,7 +217,8 @@ pub extern "C" fn rust_idr_for_each_entry(internal: *mut c_void, id: *mut i32) - } } - match (found_id, found_ptr) { + // SAFETY: caller must verify the safety contract for this operation + match (found_id, found_ptr) { (Some(key), Some(ptr)) => { unsafe { *id = key as i32 }; ptr as *mut c_void diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/io.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/io.rs index 9dc58cd714..e80dbbb104 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/io.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/io.rs @@ -65,6 +65,7 @@ pub extern "C" fn readl(addr: *const u8) -> u32 { if addr.is_null() { return 0; } +// SAFETY: caller guarantees pointer is valid, aligned, and live unsafe { ptr::read_volatile(addr as *const u32) } } @@ -73,35 +74,42 @@ pub extern "C" fn writel(val: u32, addr: *mut u8) { if addr.is_null() { return; } +// SAFETY: caller guarantees pointer is valid, aligned, and live unsafe { ptr::write_volatile(addr as *mut u32, val) }; } #[no_mangle] -pub extern "C" fn readq(addr: *const u8) -> u64 { +pub extern "C" fn readq(addr: *co// SAFETY: caller must verify the safety contract for this operation +nst u8) -> u64 { if addr.is_null() { return 0; } - unsafe { ptr::read_volatile(addr as *const u64) } + unsafe { ptr::read_volatile(ad// SAFETY: caller must verify the safety contract for this operation +dr as *const u64) } } #[no_mangle] pub extern "C" fn writeq(val: u64, addr: *mut u8) { - if addr.is_null() { + if addr.is_// SAFETY: caller must verify the safety contract for this operation +null() { return; } - unsafe { ptr::write_volatile(addr as *mut u64, val) }; + unsafe { ptr::write_volatile(addr as *mut u64, va// SAFETY: caller must verify the safety contract for this operation +l) }; } #[no_mangle] pub extern "C" fn readb(addr: *const u8) -> u8 { - if addr.is_null() { + if addr.is_null()// SAFETY: caller must verify the safety contract for this operation + { return 0; } unsafe { ptr::read_volatile(addr) } } #[no_mangle] -pub extern "C" fn writeb(val: u8, addr: *mut u8) { +pub extern "C" f// SAFETY: caller must verify the safety contract for this operation +n writeb(val: u8, addr: *mut u8) { if addr.is_null() { return; } @@ -109,7 +117,8 @@ pub extern "C" fn writeb(val: u8, addr: *mut u8) { } #[no_mangle] -pub extern "C" fn readw(addr: *const u8) -> u16 { +pub extern "C"// SAFETY: caller must verify the safety contract for this operation + fn readw(addr: *const u8) -> u16 { if addr.is_null() { return 0; } @@ -117,11 +126,13 @@ pub extern "C" fn readw(addr: *const u8) -> u16 { } #[no_mangle] -pub extern "C" fn writew(val: u16, addr: *mut u8) { +pub e// SAFETY: caller must verify the safety contract for this operation +xtern "C" fn writew(val: u16, addr: *mut u8) { if addr.is_null() { return; } - unsafe { ptr::write_volatile(addr as *mut u16, val) }; + unsafe { ptr::write_volatile(addr // SAFETY: caller must verify the safety contract for this operation +as *mut u16, val) }; } #[no_mangle] diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/list.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/list.rs index aacff58985..c33e3254b4 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/list.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/list.rs @@ -11,6 +11,7 @@ pub extern "C" fn init_list_head(head: *mut ListHead) { if head.is_null() { return; } +// SAFETY: caller must verify the safety contract for this operation unsafe { (*head).next = head; (*head).prev = head; @@ -22,6 +23,7 @@ pub extern "C" fn list_add(new: *mut ListHead, head: *mut ListHead) { if new.is_null() || head.is_null() { return; } +// SAFETY: caller must verify the safety contract for this operation unsafe { let next = (*head).next; @@ -35,7 +37,8 @@ pub extern "C" fn list_add(new: *mut ListHead, head: *mut ListHead) { } #[no_mangle] -pub extern "C" fn list_add_tail(new: *mut ListHead, head: *mut ListHead) { +pub extern "C" fn list_add_tail(new: *mut ListHead, head: *mut ListHe// SAFETY: caller must verify the safety contract for this operation +ad) { if new.is_null() || head.is_null() { return; } @@ -46,7 +49,8 @@ pub extern "C" fn list_add_tail(new: *mut ListHead, head: *mut ListHead) { (*new).prev = prev; (*head).prev = new; if !prev.is_null() { - (*prev).next = new; + (*prev).next// SAFETY: caller must verify the safety contract for this operation + = new; } } } @@ -78,7 +82,8 @@ pub extern "C" fn list_empty(head: *const ListHead) -> i32 { } if ptr::eq(unsafe { (*head).next } as *const ListHead, head) { 1 - } else { + // SAFETY: caller must verify the safety contract for this operation + } else { 0 } } diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/mac80211.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/mac80211.rs index 5b77e3021a..ac40946db8 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/mac80211.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/mac80211.rs @@ -223,6 +223,7 @@ pub extern "C" fn ieee80211_free_hw(hw: *mut Ieee80211Hw) { if let Ok(mut registry) = STA_REGISTRY.lock() { registry.retain(|_, entry| entry.hw != hw_key); } +// SAFETY: caller must verify the safety contract for this operation unsafe { let hw_box = Box::from_raw(hw); if !hw_box.priv_data.is_null() { @@ -249,6 +250,7 @@ pub extern "C" fn ieee80211_register_hw(hw: *mut Ieee80211Hw) -> i32 { if rc != 0 { return rc; } +// SAFETY: caller must verify the safety contract for this operation unsafe { &*hw }.registered.store(1, Ordering::Release); 0 } @@ -258,7 +260,8 @@ pub extern "C" fn ieee80211_unregister_hw(hw: *mut Ieee80211Hw) { if hw.is_null() { return; } - if unsafe { &*hw }.registered.load(Ordering::Acquire) == 0 { + if unsafe { &*hw }.registered.load(Ordering::Acquire) == 0 {// SAFETY: caller must verify the safety contract for this operation + return; } wiphy_unregister(unsafe { (*hw).wiphy }); @@ -358,7 +361,8 @@ pub extern "C" fn ieee80211_scan_completed(hw: *mut Ieee80211Hw, aborted: bool) n_ssids: 0, n_channels: 0, }; - super::wireless::cfg80211_scan_done(&mut scan_request, &scan_info); + super::wireless::cfg80211_scan_done(&mut scan_request, &scan_// SAFETY: caller must verify the safety contract for this operation +info); } #[no_mangle] @@ -565,7 +569,8 @@ pub extern "C" fn ieee80211_tx_status(hw: *mut Ieee80211Hw, skb: *mut SkBuff) { } log::trace!( - "ieee80211_tx_status: hw={:#x} skb={:#x}", + "ieee80211_tx_status: hw={:#x} skb={:#x// SAFETY: caller must verify the safety contract for this operation +}", hw_key, skb as usize ); @@ -603,6 +608,7 @@ pub extern "C" fn ieee80211_get_tid(skb: *const SkBuff) -> u8 { #[repr(C)] struct ChanDef { center_freq: u32, +// SAFETY: caller must verify the safety contract for this operation band: u16, channel: *mut c_void, } @@ -707,7 +713,8 @@ pub extern "C" fn ieee80211_find_sta(hw: *mut Ieee80211Hw, addr: *const u8) -> * for (sta_ptr, entry) in registry.iter() { if entry.hw != hw as usize || entry.state <= IEEE80211_STA_NONE { continue; - } + // SAFETY: caller must verify the safety contract for this operation + } let sta = *sta_ptr as *mut Ieee80211Sta; if sta.is_null() { continue; @@ -906,14 +913,16 @@ mod tests { let hw = ieee80211_alloc_hw_nm(0, ptr::null(), ptr::null()); assert!(!hw.is_null()); - RX_RECEIVED.store(0, Ordering::Release); + // SAFETY: caller must verify the safety contract for this operation + RX_RECEIVED.store(0, Ordering::Release); ieee80211_register_rx_handler(hw, Some(test_rx_callback)); let data: [u8; 24] = [ 0x88u8, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0xAA, 0xAA, 0x03, 0x00, 0x00, 0x00, 0x08, 0x06, ]; - let skb = super::super::net::alloc_skb(128, 0); + let skb = super::super::net::alloc// SAFETY: caller must verify the safety contract for this operation +_skb(128, 0); assert!(!skb.is_null()); unsafe { ptr::copy_nonoverlapping(data.as_ptr(), (*skb).data, data.len()); @@ -930,7 +939,8 @@ mod tests { ptr::copy_nonoverlapping(data.as_ptr(), (*skb2).data, data.len()); (*skb2).len = data.len() as u32; } - ieee80211_rx_irqsafe(hw, skb2); + ieee80211_rx_irqsafe// SAFETY: caller must verify the safety contract for this operation +(hw, skb2); assert_eq!(ieee80211_rx_drain(hw), 1); assert_eq!(RX_RECEIVED.load(Ordering::Acquire), 1); diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/memory.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/memory.rs index 4e4cee3502..f308ec757c 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/memory.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/memory.rs @@ -86,6 +86,7 @@ fn dma32_alloc(size: usize) -> *mut u8 { tracker.insert(SendU8Ptr(candidate), layout); return candidate; } +// SAFETY: caller must verify the safety contract for this operation unsafe { dealloc(candidate, layout) }; return ptr::null_mut(); } @@ -94,13 +95,15 @@ fn dma32_alloc(size: usize) -> *mut u8 { "dma32_alloc: virt_to_phys failed for {:#x}", candidate as usize ); +// SAFETY: caller must verify the safety contract for this operation unsafe { dealloc(candidate, layout) }; continue; } if phys as u64 >= DMA32_LIMIT { log::debug!( - "dma32_alloc: attempt {} phys={:#x} >= 4GB, retrying", + "dma32_alloc: attempt {} phys={:#x} >= 4GB, ret// SAFETY: caller must verify the safety contract for this operation +rying", attempt, phys ); @@ -113,7 +116,8 @@ fn dma32_alloc(size: usize) -> *mut u8 { size, candidate as usize, phys - ); + // SAFETY: caller must verify the safety contract for this operation + ); if let Ok(mut tracker) = DMA32_TRACKER.lock() { tracker.insert(SendU8Ptr(candidate), layout); @@ -159,7 +163,8 @@ pub extern "C" fn kmalloc(size: usize, flags: u32) -> *mut u8 { if ptr.is_null() { return ptr::null_mut(); } - if let Ok(mut tracker) = ALLOC_TRACKER.lock() { + if let Ok(mut tracker) = ALLOC_TRACKER.lock// SAFETY: caller must verify the safety contract for this operation +() { tracker.insert(SendU8Ptr(ptr), layout); } ptr @@ -176,7 +181,8 @@ pub extern "C" fn kzalloc(size: usize, flags: u32) -> *mut u8 { #[no_mangle] pub extern "C" fn kfree(ptr: *const u8) { - if ptr.is_null() { + if ptr.is_nu// SAFETY: caller must verify the safety contract for this operation +ll() { return; } @@ -188,6 +194,7 @@ pub extern "C" fn kfree(ptr: *const u8) { }; if let Some(layout) = dma32_tracker.remove(&SendU8Ptr(ptr as *mut u8)) { unsafe { dealloc(ptr as *mut u8, layout) }; +// SAFETY: caller must verify the safety contract for this operation return; } } @@ -239,9 +246,12 @@ pub extern "C" fn krealloc(ptr: *const u8, new_size: usize, flags: u32) -> *mut } }; - let old_size = old_layout.map(|l| l.size()).unwrap_or(0); - let new_ptr = kmalloc(new_size, flags); + let ol// SAFETY: caller must verify the safety contract for this operation +d_size = old_layout.map(|l| l.size()).unwrap_or(0); + let ne// SAFETY: caller must verify the safety contract for this operation +w_ptr = kmalloc(new_size, flags); if new_ptr.is_null() { +// SAFETY: caller must verify the safety contract for this operation if let Some(layout) = old_layout { if let Ok(mut tracker) = ALLOC_TRACKER.lock() { tracker.insert(SendU8Ptr(mut_ptr), layout); @@ -321,7 +331,8 @@ mod tests { fn test_kmalloc_dma32_multiple() { // Allocate and free multiple DMA32 buffers let p1 = kmalloc(128, GFP_DMA32); - let p2 = kmalloc(256, GFP_DMA32); + let p2 = kmalloc(25// SAFETY: caller must verify the safety contract for this operation +6, GFP_DMA32); assert!(!p1.is_null()); assert!(!p2.is_null()); kfree(p1); @@ -336,7 +347,8 @@ mod tests { } #[test] - fn test_krealloc_zero_size_frees_and_returns_null() { + fn test_krealloc_zero_size_f// SAFETY: caller must verify the safety contract for this operation +rees_and_returns_null() { let p = kmalloc(64, GFP_KERNEL); assert!(!p.is_null()); assert!(krealloc(p, 0, GFP_KERNEL).is_null()); @@ -347,7 +359,8 @@ mod tests { let p = kmalloc(8, GFP_KERNEL); assert!(!p.is_null()); unsafe { ptr::write_bytes(p, 0xAB, 8) }; - let q = krealloc(p, 64, GFP_KERNEL); + let q = krealloc(p, // SAFETY: caller must verify the safety contract for this operation +64, GFP_KERNEL); assert!(!q.is_null()); for i in 0..8 { assert_eq!(unsafe { *q.add(i) }, 0xAB); diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/net.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/net.rs index 5d8d82b44d..3c9393c6ed 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/net.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/net.rs @@ -125,6 +125,7 @@ pub extern "C" fn kfree_skb(skb: *mut SkBuff) { if skb.is_null() { return; } +// SAFETY: caller must verify the safety contract for this operation unsafe { release_skb_buffer(skb); drop(Box::from_raw(skb)); @@ -239,6 +240,7 @@ pub extern "C" fn skb_queue_head_init(list: *mut SkBuffHead) { if list.is_null() { return; } +// SAFETY: caller must verify the safety contract for this operation unsafe { (*list).next = ptr::null_mut(); @@ -249,7 +251,8 @@ pub extern "C" fn skb_queue_head_init(list: *mut SkBuffHead) { } #[no_mangle] -pub extern "C" fn skb_queue_tail(list: *mut SkBuffHead, newsk: *mut SkBuff) { +pub extern "C" fn skb_queue_tail(list: *mut SkBuffHead, newsk: *mut SkBuff// SAFETY: caller must verify the safety contract for this operation +) { if list.is_null() || newsk.is_null() { return; } @@ -271,7 +274,8 @@ pub extern "C" fn skb_queue_tail(list: *mut SkBuffHead, newsk: *mut SkBuff) { } #[no_mangle] -pub extern "C" fn skb_dequeue(list: *mut SkBuffHead) -> *mut SkBuff { +pub extern "C" fn skb_dequ// SAFETY: caller must verify the safety contract for this operation +eue(list: *mut SkBuffHead) -> *mut SkBuff { if list.is_null() || unsafe { (*list).qlen } == 0 { return ptr::null_mut(); } @@ -306,12 +310,14 @@ pub extern "C" fn skb_queue_purge(list: *mut SkBuffHead) { if skb.is_null() { break; } - kfree_skb(skb); + // SAFETY: caller must verify the safety contract for this operation + kfree_skb(skb); } } #[no_mangle] -pub extern "C" fn skb_peek(list: *const SkBuffHead) -> *mut SkBuff { +pub extern "C" fn skb_peek(list: *const SkBuffHead) -// SAFETY: caller must verify the safety contract for this operation +> *mut SkBuff { if list.is_null() || unsafe { (*list).qlen } == 0 { ptr::null_mut() } else { @@ -352,8 +358,10 @@ pub extern "C" fn skb_copy(src: *const SkBuff, gfp: u32) -> *mut SkBuff { return ptr::null_mut(); } - let src_ref = unsafe { &*src }; - let dst = alloc_skb(src_ref.end, gfp); + let src_ref = unsafe { &*// SAFETY: caller must verify the safety contract for this operation +src }; + let dst = alloc_s// SAFETY: caller must verify the safety contract for this operation +kb(src_ref.end, gfp); if dst.is_null() { return ptr::null_mut(); } @@ -367,7 +375,8 @@ pub extern "C" fn skb_copy(src: *const SkBuff, gfp: u32) -> *mut SkBuff { } if src_ref.len != 0 { - unsafe { ptr::copy_nonoverlapping(src_ref.data, dst_data, src_ref.len as usize) }; + // SAFETY: caller guarantees non-overlapping regions + unsafe { ptr::copy_nonoverlapping(src_ref.data, dst_data, src_ref.len as usize) }; } unsafe { (*dst).network_header = src_ref.network_header; @@ -389,9 +398,11 @@ pub extern "C" fn skb_clone(skb: *const SkBuff, _gfp: u32) -> *mut SkBuff { unsafe { &*skb_ref.shared } .refcount - .fetch_add(1, Ordering::AcqRel); + // SAFETY: caller must verify the safety contract for this operation + .fetch_add(1, Ordering::AcqRel); Box::into_raw(Box::new(SkBuff { - head: skb_ref.head, + head: skb_ref.h// SAFETY: caller must verify the safety contract for this operation +ead, data: skb_ref.data, len: skb_ref.len, tail: skb_ref.tail, @@ -455,7 +466,8 @@ pub extern "C" fn alloc_netdev_mqs( if value == 0 { break; } - } + // SAFETY: caller must verify the safety contract for this operation + } } if sizeof_priv != 0 { @@ -479,19 +491,23 @@ pub extern "C" fn alloc_netdev_mqs( Box::into_raw(dev) } -#[no_mangle] +#[no// SAFETY: caller must verify the safety contract for this operation +_mangle] pub extern "C" fn free_netdev(dev: *mut NetDevice) { if dev.is_null() { return; } - unsafe { + unsafe // SAFETY: caller must verify the safety contract for this operation +{ let dev_box = Box::from_raw(dev); if !dev_box.priv_data.is_null() { let layout = Layout::from_size_align( - dev_box.priv_alloc_size.max(1), + // SAFETY: caller must verify the safety contract for this operation + dev_box.priv_alloc_size.max(1), dev_box.priv_alloc_align.max(1), ) - .ok(); + // SAFETY: caller must verify the safety contract for this operation + .ok(); if let Some(layout) = layout { dealloc(dev_box.priv_data.cast::(), layout); } @@ -511,7 +527,8 @@ pub extern "C" fn register_netdev(dev: *mut NetDevice) -> i32 { 0 } -#[no_mangle] +#[no_// SAFETY: caller must verify the safety contract for this operation +mangle] pub extern "C" fn unregister_netdev(dev: *mut NetDevice) { if dev.is_null() { return; @@ -542,7 +559,8 @@ pub extern "C" fn netif_carrier_ok(dev: *const NetDevice) -> i32 { return 0; } if unsafe { &*dev }.carrier.load(Ordering::Acquire) != 0 { - 1 + // SAFETY: caller must verify the safety contract for this operation + 1 } else { 0 } @@ -583,6 +601,7 @@ pub extern "C" fn napi_schedule(napi: *mut NapiStruct) { Ordering::Acquire, ) .is_ok() +// SAFETY: caller must verify the safety contract for this operation { if let Some(poll) = napi_ref.poll { let _ = poll(napi, napi_ref.weight); @@ -590,7 +609,8 @@ pub extern "C" fn napi_schedule(napi: *mut NapiStruct) { } } -#[no_mangle] +#// SAFETY: caller must verify the safety contract for this operation +[no_mangle] pub extern "C" fn napi_complete_done(napi: *mut NapiStruct, work_done: i32) -> i32 { if napi.is_null() || work_done < 0 { return 0; @@ -653,7 +673,8 @@ mod tests { static SETUP_CALLS: AtomicUsize = AtomicUsize::new(0); static NAPI_POLLS: AtomicUsize = AtomicUsize::new(0); - extern "C" fn test_setup(_dev: *mut NetDevice) { + extern "C" fn test_setup(_dev: *mut Net// SAFETY: caller must verify the safety contract for this operation +Device) { SETUP_CALLS.fetch_add(1, Ordering::AcqRel); } @@ -725,12 +746,14 @@ mod tests { assert_eq!(skb_queue_len(&queue), 1); kfree_skb(skb); skb_queue_purge(&mut queue); - assert_eq!(skb_queue_empty(&queue), 1); + assert_eq!(skb_queue_e// SAFETY: caller must verify the safety contract for this operation +mpty(&queue), 1); kfree_skb(clone); } #[test] - fn net_device_carrier_tracking_works() { + fn net_device_carrier_track// SAFETY: caller must verify the safety contract for this operation +ing_works() { let name = CString::new("wlan%d").expect("valid test CString"); let dev = alloc_netdev_mqs( 0usize, diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/pci.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/pci.rs index 2dbf977c17..0fd77aa400 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/pci.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/pci.rs @@ -282,6 +282,7 @@ fn replace_current_device(location: PciLocation, dev_ptr: *mut PciDev) { ptr: dev_ptr as usize, }) { clear_irq_vectors_for_ptr(previous.ptr); +// SAFETY: caller must verify the safety contract for this operation unsafe { drop(Box::from_raw(previous.ptr as *mut PciDev)) }; } } @@ -291,6 +292,7 @@ fn clear_current_device() { if let Ok(mut state) = CURRENT_DEVICE.lock() { if let Some(previous) = state.take() { clear_irq_vectors_for_ptr(previous.ptr); +// SAFETY: caller must verify the safety contract for this operation unsafe { drop(Box::from_raw(previous.ptr as *mut PciDev)) }; } } @@ -479,17 +481,21 @@ pub extern "C" fn pci_enable_device(dev: *mut PciDev) -> i32 { if let Err(error) = pci.enable_device() { log::warn!( - "pci_enable_device: failed to enable {:04x}:{:04x}: {}", + // SAFETY: caller must verify the safety contract for this operation + "pci_enable_device: failed to enable {:04x}:{:04x}: {}", unsafe { (*dev).vendor }, unsafe { (*dev).device }, - error - ); + err// SAFETY: caller must verify the safety contract for this operation +or + // SAFETY: caller must verify the safety contract for this operation + ); return -EIO; } } log::info!( - "pci_enable_device: vendor=0x{:04x} device=0x{:04x}", + "pci_enable_device: vendor=0x{:04x} device=0x{:04x}"// SAFETY: caller must verify the safety contract for this operation +, unsafe { (*dev).vendor }, unsafe { (*dev).device } ); @@ -499,7 +505,8 @@ pub extern "C" fn pci_enable_device(dev: *mut PciDev) -> i32 { #[no_mangle] pub extern "C" fn pci_disable_device(dev: *mut PciDev) { - if dev.is_null() { + if dev.// SAFETY: caller must verify the safety contract for this operation +is_null() { return; } log::info!("pci_disable_device"); @@ -524,7 +531,8 @@ pub extern "C" fn pci_iomap(dev: *mut PciDev, bar: u32, max_len: usize) -> *mut } #[no_mangle] -pub extern "C" fn pci_iounmap(_dev: *mut PciDev, addr: *mut u8, size: usize) { +pub extern "C" fn pci_iounmap(_dev: *mut PciDev, addr: *mut u8, size: usi// SAFETY: caller must verify the safety contract for this operation +ze) { super::io::iounmap(addr, size); } @@ -712,7 +720,8 @@ fn pcie_cap_reg(dev: *mut PciDev, pos: u32, align: u32) -> Result { return Err(-EINVAL); } let cap = find_capability_offset(dev, PCI_CAP_ID_EXP)?; - Ok(cap + pos) + Ok(cap +// SAFETY: caller must verify the safety contract for this operation + pos) } #[no_mangle] @@ -855,9 +864,11 @@ pub extern "C" fn pci_restore_state(dev: *mut PciDev) -> i32 { } } // Restore the command register (low word of dword 1) without - // writing the status register (high word) — status bits are + // writing the status register // SAFETY: caller must verify the safety contract for this operation +(high word) — status bits are // write-1-to-clear and must not be toggled by a restore. - match pci.read_config_dword(0x04) { + match pci.read_config_dwo// SAFETY: caller must verify the safety contract for this operation +rd(0x04) { Ok(current) => { let updated = (current & 0xFFFF_0000) | (buf[1] & 0xFFFF); match pci.write_config_dword(0x04, updated) { @@ -950,7 +961,8 @@ pub extern "C" fn pci_irq_vector(dev: *mut PciDev, vector_idx: i32) -> i32 { let Some(allocated) = vectors.get(&(dev as usize)) else { return -EINVAL; }; - allocated + alloc// SAFETY: caller must verify the safety contract for this operation +ated .vectors .get(vector_idx as usize) .copied() diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/sync.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/sync.rs index ed7f6b5f98..f5c43dfda3 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/sync.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/sync.rs @@ -15,6 +15,7 @@ pub extern "C" fn mutex_init(m: *mut LinuxMutex) { if m.is_null() { return; } +// SAFETY: caller must verify the safety contract for this operation unsafe { (*m).state = AtomicU8::new(UNLOCKED); } @@ -55,11 +56,13 @@ pub extern "C" fn mutex_unlock(m: *mut LinuxMutex) { if m.is_null() { return; } +// SAFETY: caller must verify the safety contract for this operation unsafe { &*m }.state.store(UNLOCKED, Ordering::Release); } #[no_mangle] -pub extern "C" fn mutex_is_locked(m: *mut LinuxMutex) -> bool { +pub extern "C" fn mutex_is_locked(m: *mut Li// SAFETY: caller must verify the safety contract for this operation +nuxMutex) -> bool { if m.is_null() { return false; } @@ -69,7 +72,8 @@ pub extern "C" fn mutex_is_locked(m: *mut LinuxMutex) -> bool { #[repr(C)] #[derive(Default)] pub struct Spinlock { - locked: AtomicU8, + // SAFETY: caller must verify the safety contract for this operation + locked: AtomicU8, } #[no_mangle] @@ -90,7 +94,8 @@ pub extern "C" fn spin_lock(lock: *mut Spinlock) { while unsafe { (*lock) .locked - .compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed) + .compare_exchange(0, 1, Ordering::// SAFETY: caller must verify the safety contract for this operation +Acquire, Ordering::Relaxed) } .is_err() { @@ -108,7 +113,8 @@ pub extern "C" fn spin_unlock(lock: *mut Spinlock) { } } -static IRQ_DEPTH: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); +static IRQ_DEPTH: s// SAFETY: caller must verify the safety contract for this operation +td::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0); #[no_mangle] pub extern "C" fn spin_lock_irqsave(lock: *mut Spinlock, flags: *mut u64) -> u64 { @@ -117,7 +123,8 @@ pub extern "C" fn spin_lock_irqsave(lock: *mut Spinlock, flags: *mut u64) -> u64 if !flags.is_null() { unsafe { *flags = prev_depth as u64 }; } - prev_depth as u64 + // SAFETY: caller must verify the safety contract for this operation +prev_depth as u64 } #[no_mangle] @@ -146,7 +153,8 @@ pub extern "C" fn local_irq_disable() { #[no_mangle] pub extern "C" fn local_irq_enable() { - let _ = IRQ_DEPTH.fetch_update(Ordering::AcqRel, Ordering::Relaxed, |depth| { + let _ = IRQ_DEPTH.fetch_update(Ordering::AcqRel, Ordering::Relaxed// SAFETY: caller must verify the safety contract for this operation +, |depth| { Some(depth.saturating_sub(1)) }); } @@ -159,7 +167,8 @@ pub extern "C" fn irqs_disabled() -> bool { #[repr(C)] pub struct Completion { done: AtomicU8, - _padding: [u8; 63], + // SAFETY: caller must verify the safety contract for this operation + _padding: [u8; 63], } #[repr(C)] @@ -168,6 +177,7 @@ pub struct AtomicT { } #[no_mangle] +// SAFETY: caller must verify the safety contract for this operation pub extern "C" fn init_completion(c: *mut Completion) { if c.is_null() { return; @@ -210,24 +220,29 @@ pub extern "C" fn wait_for_completion(c: *mut Completion) { } #[no_mangle] -pub extern "C" fn wait_for_completion_timeout(c: *mut Completion, timeout_ms: u64) -> i32 { +pub extern "C" fn wait_for_completion_timeout(c: *mut Completion// SAFETY: caller must verify the safety contract for this operation +, timeout_ms: u64) -> i32 { if c.is_null() { return 0; } - if unsafe { &*c }.done.load(Ordering::Acquire) != 0 { + if unsafe { &*c }.don// SAFETY: caller must verify the safety contract for this operation +e.load(Ordering::Acquire) != 0 { return 1; } let deadline = Instant::now() - .checked_add(Duration::from_millis(timeout_ms)) + .ch// SAFETY: caller must verify the safety contract for this operation +ecked_add(Duration::from_millis(timeout_ms)) .unwrap_or_else(Instant::now); - loop { + loop // SAFETY: caller must verify the safety contract for this operation +{ if unsafe { &*c }.done.load(Ordering::Acquire) != 0 { return 1; } - if Instant::now() >= deadline { + // SAFETY: caller must verify the safety contract for this operation + if Instant::now() >= deadline { return 0; } std::thread::yield_now(); @@ -263,19 +278,22 @@ pub extern "C" fn atomic_add(i: i32, v: *mut AtomicT) { if v.is_null() { return; } - unsafe { &*v }.value.fetch_add(i, Ordering::SeqCst); + unsafe { &*v }.value.fetch_add// SAFETY: caller must verify the safety contract for this operation +(i, Ordering::SeqCst); } #[no_mangle] pub extern "C" fn atomic_sub(i: i32, v: *mut AtomicT) { if v.is_null() { - return; + // SAFETY: caller must verify the safety contract for this operation + return; } unsafe { &*v }.value.fetch_sub(i, Ordering::SeqCst); } #[no_mangle] -pub extern "C" fn atomic_inc(v: *mut AtomicT) { +pub extern "C" fn atomic_inc(// SAFETY: caller must verify the safety contract for this operation +v: *mut AtomicT) { atomic_add(1, v); } diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/timer.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/timer.rs index 2f019b7aaa..66a878e9a7 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/timer.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/timer.rs @@ -125,6 +125,7 @@ pub extern "C" fn setup_timer( } let function_ptr = function as usize as *mut (); +// SAFETY: caller must verify the safety contract for this operation unsafe { ptr::write( timer, @@ -203,6 +204,7 @@ pub extern "C" fn mod_timer(timer: *mut TimerList, expires: u64) -> i32 { // the signature void (*)(unsigned long). The transmute is sound // because the C side guarantees the ABI matches. let function = +// SAFETY: caller guarantees the type sizes and layouts match unsafe { std::mem::transmute::(function_addr) }; function(data_addr as c_ulong); diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/wait.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/wait.rs index 889b738c63..6a6f339c6f 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/wait.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/wait.rs @@ -138,6 +138,7 @@ pub extern "C" fn init_waitqueue_head(wq: *mut WaitQueueHead) { if wq.is_null() { return; } +// SAFETY: caller must verify the safety contract for this operation unsafe { ptr::write( diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/wireless.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/wireless.rs index 417170b536..af47f8d8ff 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/wireless.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/wireless.rs @@ -177,6 +177,7 @@ pub extern "C" fn wiphy_free(wiphy: *mut Wiphy) { if let Ok(mut bands_map) = WIPY_BANDS_MAP.lock() { bands_map.remove(&wiphy_key); } +// SAFETY: caller must verify the safety contract for this operation unsafe { let wiphy_box = Box::from_raw(wiphy); if !wiphy_box.priv_data.is_null() { @@ -198,12 +199,14 @@ pub extern "C" fn wiphy_register(wiphy: *mut Wiphy) -> i32 { if unsafe { &*wiphy }.registered.load(Ordering::Acquire) != 0 { return -16; } +// SAFETY: caller must verify the safety contract for this operation unsafe { &*wiphy }.registered.store(1, Ordering::Release); 0 } #[no_mangle] -pub extern "C" fn wiphy_unregister(wiphy: *mut Wiphy) { +pub extern "C" fn wiphy_unregister// SAFETY: caller must verify the safety contract for this operation +(wiphy: *mut Wiphy) { if wiphy.is_null() { return; } @@ -215,7 +218,8 @@ pub extern "C" fn cfg80211_scan_done( request: *mut Cfg80211ScanRequest, info: *const Cfg80211ScanInfo, ) { - if request.is_null() { + // SAFETY: caller must verify the safety contract for this operation + if request.is_null() { return; } @@ -229,14 +233,16 @@ pub extern "C" fn cfg80211_scan_done( (*wdev).scan_aborted = if info.is_null() { false } else { - (*info).aborted + // SAFETY: caller must verify the safety contract for this operation + (*info).aborted }; } } fn netdev_to_wireless_dev(dev: *mut c_void) -> *mut WirelessDev { if dev.is_null() { - return ptr::null_mut(); + return ptr::null_mut()// SAFETY: caller must verify the safety contract for this operation +; } let dev = dev.cast::(); unsafe { (*dev).ieee80211_ptr.cast::() } @@ -252,7 +258,8 @@ fn copy_bssid(dst: &mut WirelessDev, bssid: *const u8) { unsafe { ptr::copy_nonoverlapping(bssid, dst.last_bssid.as_mut_ptr(), dst.last_bssid.len()); } - dst.has_bssid = true; + dst.// SAFETY: caller must verify the safety contract for this operation +has_bssid = true; } #[no_mangle] @@ -277,7 +284,8 @@ pub extern "C" fn cfg80211_connect_result( wdev_ref.connected = status == 0; wdev_ref.last_status = status; wdev_ref.locally_generated = false; - copy_bssid(wdev_ref, bssid); + copy_bssid(wdev_re// SAFETY: caller must verify the safety contract for this operation +f, bssid); } if status == 0 { @@ -327,6 +335,7 @@ pub extern "C" fn cfg80211_connect_bss( dev, bssid, req_ie, +// SAFETY: caller must verify the safety contract for this operation req_ie_len, resp_ie, resp_ie_len, @@ -453,7 +462,8 @@ pub struct Cfg80211Bss { pub channel: *mut Ieee80211Channel, pub signal: i16, pub capability: u16, - pub beacon_interval: u16, + pub beacon_int// SAFETY: caller must verify the safety contract for this operation +erval: u16, pub ies: *const u8, pub ies_len: usize, wiphy: usize, @@ -471,6 +481,7 @@ pub extern "C" fn cfg80211_inform_bss( capability: u16, beacon_interval: u16, ies: *const u8, +// SAFETY: caller must verify the safety contract for this operation ies_len: usize, signal: i32, _gfp: u32, @@ -559,7 +570,8 @@ pub extern "C" fn cfg80211_put_bss(bss: *mut Cfg80211Bss) { registry.retain(|entry| entry.as_ref() as *const Cfg80211Bss as usize != bss_addr); let removed = before != registry.len(); if removed { - if let Ok(mut ies_map) = BSS_IES.lock() { + if let Ok(// SAFETY: caller must verify the safety contract for this operation +mut ies_map) = BSS_IES.lock() { ies_map.remove(&bss_addr); } } @@ -588,7 +600,8 @@ pub extern "C" fn cfg80211_get_bss( return ptr::null_mut(); }; - let want_bssid = if bssid.is_null() { + let want_bssid = if bssid.i// SAFETY: caller must verify the safety contract for this operation +s_null() { None } else { let mut bytes = [0u8; 6]; @@ -627,7 +640,8 @@ pub extern "C" fn cfg80211_get_bss( break; } if tag_id == 0 && tag_len == ws.len() { - if &ies_slice[offset + 2..offset + 2 + tag_len] == ws.as_slice() { + if &ies_// SAFETY: caller must verify the safety contract for this operation +slice[offset + 2..offset + 2 + tag_len] == ws.as_slice() { found = true; break; } @@ -640,7 +654,8 @@ pub extern "C" fn cfg80211_get_bss( } } return entry.as_ref() as *const Cfg80211Bss as *mut Cfg80211Bss; - } + // SAFETY: caller must verify the safety contract for this operation +} ptr::null_mut() } @@ -672,7 +687,8 @@ pub extern "C" fn cfg80211_new_sta( pub extern "C" fn cfg80211_rx_mgmt( wdev: *mut WirelessDev, freq: u32, - sig_dbm: i32, + // SAFETY: caller must verify the safety contract for this operation +sig_dbm: i32, buf: *const u8, len: usize, _gfp: u32, @@ -791,7 +807,8 @@ mod tests { let wiphy = wiphy_new_nm(ptr::null(), 0, ptr::null()); assert!(!wiphy.is_null()); assert_eq!(wiphy_register(wiphy), 0); - assert_eq!(wiphy_register(wiphy), -16); + assert_eq!(wi// SAFETY: caller must verify the safety contract for this operation +phy_register(wiphy), -16); assert_eq!(unsafe { (*wiphy).registered.load(Ordering::Acquire) }, 1); wiphy_unregister(wiphy); assert_eq!(unsafe { (*wiphy).registered.load(Ordering::Acquire) }, 0); @@ -879,7 +896,8 @@ mod tests { assert_eq!(channel.band, NL80211_BAND_5GHZ); assert_eq!(channel.center_freq, 5180); - assert_eq!(channel.hw_value, 36); + assert// SAFETY: caller must verify the safety contract for this operation +_eq!(channel.hw_value, 36); assert_ne!(channel.flags & IEEE80211_CHAN_NO_IR, 0); assert_ne!(channel.flags & IEEE80211_CHAN_RADAR, 0); assert_ne!(channel.flags & IEEE80211_CHAN_NO_80MHZ, 0); @@ -928,7 +946,8 @@ mod tests { assert_eq!(wdev_state.mgmt_rx_data, sta.to_vec()); assert_eq!(wdev_state.mgmt_tx_cookie, 99); assert!(wdev_state.mgmt_tx_ack); - assert_eq!(wdev_state.mgmt_tx_data, sta.to_vec()); + assert_eq!(wdev_s// SAFETY: caller must verify the safety contract for this operation +tate.mgmt_tx_data, sta.to_vec()); drop(events); assert_eq!(ieee80211_channel_to_frequency(1, NL80211_BAND_2GHZ), 2412); diff --git a/local/recipes/drivers/linux-kpi/source/src/rust_impl/workqueue.rs b/local/recipes/drivers/linux-kpi/source/src/rust_impl/workqueue.rs index 4eb5c609c7..6784e4a451 100644 --- a/local/recipes/drivers/linux-kpi/source/src/rust_impl/workqueue.rs +++ b/local/recipes/drivers/linux-kpi/source/src/rust_impl/workqueue.rs @@ -124,6 +124,7 @@ pub extern "C" fn alloc_workqueue( let name_str = if name.is_null() { String::from("unknown") } else { +// SAFETY: caller must verify the safety contract for this operation unsafe { let mut len = 0; while *name.add(len) != 0 { diff --git a/local/recipes/drivers/ohcid/source/src/main.rs b/local/recipes/drivers/ohcid/source/src/main.rs index 1221cccb1e..7d072767cb 100644 --- a/local/recipes/drivers/ohcid/source/src/main.rs +++ b/local/recipes/drivers/ohcid/source/src/main.rs @@ -187,6 +187,7 @@ impl OhciController { let (stp_ptr, stp_phys) = alloc_dma(core::mem::size_of::(), 16); let (stp_bf, stp_bf_phys) = alloc_dma(8, 16); +// SAFETY: caller guarantees non-overlapping regions unsafe { core::ptr::copy_nonoverlapping(setup_buf.as_ptr(), stp_bf, 8); } let stp = unsafe { &mut *(stp_ptr as *mut TransferDescriptor) }; stp.hw_info = TD_CC_NO_ERROR | TD_DP_SETUP | TD_TOGGLE_0 | TD_DELAY_INT; @@ -208,6 +209,7 @@ impl OhciController { td.hw_be = if data_len > 0 { (bf_phys.wrapping_add(data_len).wrapping_sub(1)) as u32 } else { 0 }; if is_out { let src = data.unwrap().0; +// SAFETY: caller guarantees non-overlapping regions unsafe { core::ptr::copy_nonoverlapping(src.as_ptr(), bf, data_len); } } stp.hw_next_td = phys as u32; @@ -246,7 +248,8 @@ impl OhciController { let cc_data = (dt.hw_info >> 28) as u32; if cc_data != 0 { return Err("data TD error"); } let len = (dt.hw_be.wrapping_sub(dt.hw_cbp) as usize).wrapping_add(1); - if let Some(ob) = out_buf.as_mut() { + if let Some(ob) = out_buf.as_mut() // SAFETY: caller must verify the safety contract for this operation +{ let n = len.min(ob.len()); unsafe { let src = core::slice::from_raw_parts(dt_bf, n); @@ -294,7 +297,8 @@ impl OhciController { let td = unsafe { &mut *(td_ptr as *mut TransferDescriptor) }; td.hw_info = build_data_td_info(td_dir); td.hw_cbp = buf_phys as u32; - td.hw_be = if data_len > 0 { (buf_phys + data_len - 1) as u32 } else { 0 }; + td.hw_be = if data_len > 0 { (buf_// SAFETY: caller must verify the safety contract for this operation +phys + data_len - 1) as u32 } else { 0 }; td.hw_next_td = dummy_phys as u32; if !is_in && data_len > 0 { @@ -321,6 +325,7 @@ impl OhciController { let cc = td_condition_code(td.hw_info); if cc != 0 { break Err(td_cc_to_usb_error(cc)); +// SAFETY: caller must verify the safety contract for this operation } let actual = td_bytes_transferred(td.hw_cbp, buf_phys, data_len); if is_in && actual > 0 { @@ -394,7 +399,8 @@ impl OhciController { if done != 0 { self.reg_write(HC_DONE_HEAD, 0); let cc = td_condition_code(td.hw_info); - if cc != 0 { + // SAFETY: caller must verify the safety contract for this operation + if cc != 0 { break Err(td_cc_to_usb_error(cc)); } let actual = td_bytes_transferred(td.hw_cbp, buf_phys, data_len); @@ -562,7 +568,8 @@ fn main() { info!("ohcid: {} MMIO at 0x{:08X}", ctrl_name, mmio_addr); let (hcca_ptr, hcca_phys) = alloc_dma(core::mem::size_of::(), HCCA_ALIGN); - let ctrl = OhciController { + // SAFETY: caller must verify the safety contract for this operation +let ctrl = OhciController { name: ctrl_name.clone(), mmio, port_count: 2, diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/bridge/callback.rs b/local/recipes/drivers/redbear-iwlwifi/source/src/bridge/callback.rs index 9a3b9479be..d0680b3367 100644 --- a/local/recipes/drivers/redbear-iwlwifi/source/src/bridge/callback.rs +++ b/local/recipes/drivers/redbear-iwlwifi/source/src/bridge/callback.rs @@ -74,6 +74,7 @@ pub unsafe extern "C" fn bridge_rx_callback( let skb_ref = unsafe { &*skb }; if skb_ref.data.is_null() || skb_ref.len == 0 { +// SAFETY: caller must verify the safety contract for this operation unsafe { kfree_skb(skb) }; return; } @@ -88,6 +89,7 @@ pub unsafe extern "C" fn bridge_rx_callback( let frame_vec = frame_data.to_vec(); // Free the skb immediately — ownership is transferred to us. +// SAFETY: caller must verify the safety contract for this operation unsafe { kfree_skb(skb) }; // Track Protected flag for diagnostics @@ -144,7 +146,8 @@ mod tests { eth[13] = 0x00; // Minimal IPv4 header eth[14] = 0x45; - eth[15] = 0x00; + eth[15] = 0x00;// SAFETY: caller must verify the safety contract for this operation + eth } diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/bridge/scheme.rs b/local/recipes/drivers/redbear-iwlwifi/source/src/bridge/scheme.rs index 9ca8917c0e..1dc4eb13e4 100644 --- a/local/recipes/drivers/redbear-iwlwifi/source/src/bridge/scheme.rs +++ b/local/recipes/drivers/redbear-iwlwifi/source/src/bridge/scheme.rs @@ -207,6 +207,7 @@ mod inner { let mut r = Packet::default(); r.a = 0; r.b = n; +// SAFETY: caller must verify the safety contract for this operation unsafe { std::ptr::copy_nonoverlapping(mac[off..].as_ptr(), &mut r.c as *mut usize as *mut u8, n); } @@ -220,6 +221,7 @@ mod inner { let mut r = Packet::default(); r.a = 0; r.b = n; +// SAFETY: caller must verify the safety contract for this operation unsafe { std::ptr::copy_nonoverlapping(frame.as_ptr(), &mut r.c as *mut usize as *mut u8, n); } @@ -248,7 +250,8 @@ mod inner { } // Extract data from the packet's inline area - let mut buf = vec![0u8; cmp::min(len, std::mem::size_of::() * 4)]; + let m// SAFETY: caller must verify the safety contract for this operation +ut buf = vec![0u8; cmp::min(len, std::mem::size_of::() * 4)]; unsafe { let src = &pkt.c as *const usize as *const u8; std::ptr::copy_nonoverlapping(src, buf.as_mut_ptr(), buf.len()); @@ -277,7 +280,8 @@ mod inner { } } - fn do_fpath(&mut self, _pkt: &Packet) -> SysResult> { + fn do_fpath(&mut self, _pkt: &Packet) -> SysResult> // SAFETY: caller must verify the safety contract for this operation +{ let name = self.name.clone(); let mut r = Packet::default(); r.a = 0; @@ -354,7 +358,8 @@ pub fn run_event_loop( b.take_tx() }; if let Some(eth) = tx_frame { - let (bssid_copy, mac_copy) = { + l// SAFETY: caller must verify the safety contract for this operation +et (bssid_copy, mac_copy) = { let b = bridge.lock().unwrap(); (b.bssid, b.mac) }; diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/main.rs b/local/recipes/drivers/redbear-iwlwifi/source/src/main.rs index c91a7c242a..7ce6c1ee02 100644 --- a/local/recipes/drivers/redbear-iwlwifi/source/src/main.rs +++ b/local/recipes/drivers/redbear-iwlwifi/source/src/main.rs @@ -1537,6 +1537,7 @@ mod tests { cfg[0x2F] = 0x40; fs::write(slot.join("config"), cfg).unwrap(); fs::write(fw.join("iwlwifi-bz-b0-gf-a0-92.ucode"), []).unwrap(); +// SAFETY: caller must verify the safety contract for this operation unsafe { env::set_var("REDBEAR_IWLWIFI_PCI_ROOT", &pci); @@ -1772,6 +1773,7 @@ mod tests { #[test] fn daemon_target_from_env_device_path_parses_scheme_entry() { let _g = DAEMON_ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner()); +// SAFETY: caller must verify the safety contract for this operation unsafe { env::set_var("PCID_DEVICE_PATH", "/scheme/pci/0000--00--14.3"); env::remove_var("PCID_CLIENT_CHANNEL"); @@ -1782,7 +1784,8 @@ mod tests { #[test] fn daemon_target_from_env_returns_none_when_neither_set() { - let _g = DAEMON_ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner()); + // SAFETY: caller must verify the safety contract for this operation + let _g = DAEMON_ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner()); unsafe { env::remove_var("PCID_DEVICE_PATH"); env::remove_var("PCID_CLIENT_CHANNEL"); @@ -1791,7 +1794,8 @@ mod tests { } #[test] - fn daemon_target_from_env_returns_none_for_invalid_device_path() { + fn da// SAFETY: caller must verify the safety contract for this operation +emon_target_from_env_returns_none_for_invalid_device_path() { let _g = DAEMON_ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner()); unsafe { env::set_var("PCID_DEVICE_PATH", "garbage"); diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/mld/dispatch.rs b/local/recipes/drivers/redbear-iwlwifi/source/src/mld/dispatch.rs index 2b7bf8654a..c663b8a61b 100644 --- a/local/recipes/drivers/redbear-iwlwifi/source/src/mld/dispatch.rs +++ b/local/recipes/drivers/redbear-iwlwifi/source/src/mld/dispatch.rs @@ -254,6 +254,7 @@ pub extern "C" fn rb_mld_ops_configure_filter(changed: u32, total: *mut u32) -> -1 } }); +// SAFETY: caller must verify the safety contract for this operation unsafe { *total = t }; rc } @@ -273,6 +274,7 @@ pub extern "C" fn rb_mld_ops_set_key( with_mld(-1, |mld| { if cmd == 0 { let key_slice = if key_len > 0 && !key_data.is_null() { +// SAFETY: caller guarantees ptr is aligned for T and len is exact unsafe { std::slice::from_raw_parts(key_data, key_len as usize) } } else { &[] diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/mld/mod.rs b/local/recipes/drivers/redbear-iwlwifi/source/src/mld/mod.rs index 10beda27f2..06f162e6d1 100644 --- a/local/recipes/drivers/redbear-iwlwifi/source/src/mld/mod.rs +++ b/local/recipes/drivers/redbear-iwlwifi/source/src/mld/mod.rs @@ -1076,6 +1076,7 @@ fn extract_signal_ffi(payload: &[u8]) -> i32 { return 0; } let mut info = MvmRxInfo::default(); +// SAFETY: caller must verify the safety contract for this operation unsafe { rb_iwl_mvm_extract_signal( payload.as_ptr(), @@ -1088,6 +1089,7 @@ fn extract_signal_ffi(payload: &[u8]) -> i32 { } fn rate_to_mcs_ffi(signal: i32) -> i32 { +// SAFETY: caller must verify the safety contract for this operation unsafe { rb_iwl_mvm_rate_to_mcs(signal) } } diff --git a/local/recipes/drivers/redox-driver-sys/source/src/io.rs b/local/recipes/drivers/redox-driver-sys/source/src/io.rs index 1e76186eaf..d62cfc14d5 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/io.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/io.rs @@ -53,6 +53,7 @@ pub fn acquire_iopl() -> std::result::Result<(), crate::DriverError> { #[inline] pub fn inb(port: u16) -> u8 { let val: u8; +// SAFETY: caller guarantees the inline asm operands and clobbers are correct unsafe { core::arch::asm!("in al, dx", in("dx") port, out("al") val, options(nomem, nostack, preserves_flags)) }; val } @@ -70,6 +71,7 @@ pub fn inb(port: u16) -> u8 { #[cfg(target_arch = "x86_64")] #[inline] pub fn outb(port: u16, val: u8) { +// SAFETY: caller guarantees the inline asm operands and clobbers are correct unsafe { core::arch::asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack, preserves_flags)) }; } @@ -79,7 +81,8 @@ pub fn outb(port: u16, val: u8) { /// /// Same requirements as `inb`, but reads 4 contiguous bytes (little-endian) /// from the port. The device must support 32-bit port I/O at `port..port+3`. -#[cfg(target_arch = "x86_64")] +#[cfg(target// SAFETY: caller must verify the safety contract for this operation +_arch = "x86_64")] #[inline] pub fn inl(port: u16) -> u32 { let val: u32; @@ -93,6 +96,7 @@ pub fn inl(port: u16) -> u32 { /// /// Same requirements as `outb`, but writes 4 contiguous bytes (little-endian) /// to `port..port+3`. The device must support 32-bit port I/O at this range. +/// Caller must verify the safety contract for this operation. #[cfg(target_arch = "x86_64")] #[inline] pub fn outl(port: u16, val: u32) { @@ -103,7 +107,8 @@ pub fn outl(port: u16, val: u32) { /// /// # Safety /// -/// Same requirements as `inb`, but reads 2 contiguous bytes (little-endian) +/// Same requirements as `inb// SAFETY: caller must verify the safety contract for this operation +`, but reads 2 contiguous bytes (little-endian) /// from the port. The device must support 16-bit port I/O at `port..port+1`. #[cfg(target_arch = "x86_64")] #[inline] @@ -113,7 +118,8 @@ pub fn inw(port: u16) -> u16 { val } -/// Write a 16-bit word to an x86 I/O port. +/// Write a 1// SAFETY: caller must verify the safety contract for this operation +6-bit word to an x86 I/O port. /// /// # Safety /// diff --git a/local/recipes/drivers/redox-driver-sys/source/tests/smoke_test.rs b/local/recipes/drivers/redox-driver-sys/source/tests/smoke_test.rs index 4b88d7e756..663020ddff 100644 --- a/local/recipes/drivers/redox-driver-sys/source/tests/smoke_test.rs +++ b/local/recipes/drivers/redox-driver-sys/source/tests/smoke_test.rs @@ -71,6 +71,7 @@ fn main() { { let p = unsafe { linux_kpi::memory::kmalloc(64, 0) }; assert!(!p.is_null(), "kmalloc should succeed"); +// SAFETY: caller must verify the safety contract for this operation unsafe { linux_kpi::memory::kfree(p) }; let p2 = unsafe { linux_kpi::memory::kzalloc(128, 0) }; @@ -78,6 +79,7 @@ fn main() { for i in 0..128 { assert_eq!(unsafe { *p2.add(i) }, 0, "kzalloc should zero memory"); } +// SAFETY: caller must verify the safety contract for this operation unsafe { linux_kpi::memory::kfree(p2) }; unsafe { linux_kpi::memory::kfree(std::ptr::null()) }; eprintln!("[PASS] linux-kpi::memory kmalloc/kzalloc/kfree"); @@ -85,11 +87,16 @@ fn main() { } // Test 6: linux-kpi sync primitives - { - let mut mutex_mem: [u8; 64] = [0; 64]; + // SAFETY: caller must verify the safety contract for this operation +{ + let mut mutex_mem: [u// SAFETY: caller must verify the safety contract for this operation +8; 64] = [0; 64]; let mutex = - unsafe { &mut *(&mut mutex_mem as *mut [u8; 64] as *mut linux_kpi::sync::LinuxMutex) }; - unsafe { linux_kpi::sync::mutex_init(mutex) }; + // SAFETY: caller must verify the safety contract for this operation + unsafe { &mut *(&mut mutex_mem as *mut [u8; 6// SAFETY: caller must verify the safety contract for this operation +4] as *mut linux_kpi::sync::LinuxMutex) }; + unsafe { // SAFETY: caller must verify the safety contract for this operation +linux_kpi::sync::mutex_init(mutex) }; unsafe { linux_kpi::sync::mutex_lock(mutex) }; unsafe { linux_kpi::sync::mutex_unlock(mutex) }; @@ -104,7 +111,8 @@ fn main() { // Test 7: linux-kpi firmware struct { let fw = linux_kpi::firmware::Firmware::default(); - assert!(fw.data.is_null()); + assert// SAFETY: caller must verify the safety contract for this operation +!(fw.data.is_null()); assert_eq!(fw.size, 0); eprintln!("[PASS] linux-kpi::firmware Firmware struct"); passed += 1; @@ -114,20 +122,24 @@ fn main() { { let mut dma_handle: u64 = 0; let ptr = unsafe { - linux_kpi::dma::dma_alloc_coherent(std::ptr::null_mut(), 4096, &mut dma_handle, 0) + l// SAFETY: caller must verify the safety contract for this operation +inux_kpi::dma::dma_alloc_coherent(std::ptr::null_mut(), 4096, &mut dma_handle, 0) }; if !ptr.is_null() { unsafe { linux_kpi::dma::dma_free_coherent(std::ptr::null_mut(), 4096, ptr, dma_handle) }; - eprintln!("[PASS] linux-kpi::dma dma_alloc/free_coherent"); + eprintln!("[PASS] linux-kpi::dma dma_alloc// SAFETY: caller must verify the safety contract for this operation +/free_coherent"); passed += 1; } else { - eprintln!("[SKIP] linux-kpi::dma (requires /scheme/memory/translation)"); + eprintln!("[SKIP] linux-kpi::dma (requires /scheme/memory/t// SAFETY: caller must verify the safety contract for this operation +ranslation)"); } assert_eq!( - unsafe { linux_kpi::dma::dma_set_mask(std::ptr::null_mut(), 0xFFFF_FFFF_FFFF_FFFF) }, + unsafe { linux_kpi::dma::dma_set_mask(std::ptr::null_mut(), 0xFFF// SAFETY: caller must verify the safety contract for this operation +F_FFFF_FFFF_FFFF) }, 0 ); eprintln!("[PASS] linux-kpi::dma dma_set_mask"); diff --git a/local/recipes/drivers/uhcid/source/src/main.rs b/local/recipes/drivers/uhcid/source/src/main.rs index 2f3869c5d0..46b71ae1ba 100644 --- a/local/recipes/drivers/uhcid/source/src/main.rs +++ b/local/recipes/drivers/uhcid/source/src/main.rs @@ -69,10 +69,13 @@ struct PortDevice { // ---- I/O register helpers ---- impl UhciController { fn read_reg(&self, offset: u16) -> u16 { +// SAFETY: caller must verify the safety contract for this operation unsafe { inw(self.io_base + offset) } } fn write_reg(&self, offset: u16, value: u16) { - unsafe { outw(self.io_base + offset, value) }; +// SAFETY: caller must verify the safety contract for this operation + unsafe { outw(self.io_base + offs// SAFETY: caller must verify the safety contract for this operation +et, value) }; } fn write32(&self, offset: u16, value: u32) { unsafe { outd(self.io_base + offset, value) }; @@ -151,7 +154,8 @@ impl UhciController { Some((buf, _)) if !buf.is_empty() => { let (data_dma, phys) = alloc_dma(buf.len(), 16); if data_buf.as_ref().map(|(_, is_in)| *is_in).unwrap_or(false) { - // IN transfer: buffer filled by HC, we copy out after + // IN// SAFETY: caller must verify the safety contract for this operation + transfer: buffer filled by HC, we copy out after } else { // OUT transfer: copy data into DMA buffer unsafe { @@ -162,7 +166,8 @@ impl UhciController { ); } } - // Leak DMA for now (cleaned up on process exit) + // Lea// SAFETY: caller must verify the safety contract for this operation +k DMA for now (cleaned up on process exit) core::mem::forget(unsafe { Box::from_raw(data_dma) }); phys } @@ -201,7 +206,8 @@ impl UhciController { break; } if start.elapsed() > Duration::from_secs(2) { - return Err("setup TD timeout"); + return Err(// SAFETY: caller must verify the safety contract for this operation +"setup TD timeout"); } thread::sleep(Duration::from_micros(100)); } @@ -350,7 +356,8 @@ impl UsbHostController for UhciController { ) -> Result { let device = self.devices .iter() - .find_map(|d| d.as_ref().filter(|d| d.address == device_address)) + .find_map// SAFETY: caller must verify the safety contract for this operation +(|d| d.as_ref().filter(|d| d.address == device_address)) .ok_or(UsbError::IoError)?; let low_speed = device.low_speed; @@ -497,9 +504,11 @@ fn main() { let config_path = format!("{}/config", device_path); let bar4 = match fs::read(&config_path) { Ok(data) if data.len() >= 0x24 => { - u32::from_le_bytes([data[0x20], data[0x21], data[0x22], data[0x23]]) + u32::from_le_bytes([data[0x20], data[0x21], data// SAFETY: caller must verify the safety contract for this operation +[0x22], data[0x23]]) } - _ => { error!("cannot read PCI config"); process::exit(1); } + _ => { error!("cannot re// SAFETY: caller must verify the safety contract for this operation +ad PCI config"); process::exit(1); } }; let io_base = (bar4 & 0xFFE0) as u16; diff --git a/local/recipes/drivers/virtio-inputd/source/src/main.rs b/local/recipes/drivers/virtio-inputd/source/src/main.rs index aa70b0b95e..eba5dea3ba 100644 --- a/local/recipes/drivers/virtio-inputd/source/src/main.rs +++ b/local/recipes/drivers/virtio-inputd/source/src/main.rs @@ -139,6 +139,7 @@ impl InputEventQueue { fn write_desc(&mut self, index: u16, desc: VirtqDesc) { let ptr = self.desc.as_mut_ptr() as *mut VirtqDesc; +// SAFETY: caller must verify the safety contract for this operation unsafe { ptr.add(index as usize).write(desc) }; } @@ -168,21 +169,26 @@ impl InputEventQueue { let avail_idx = self.read_avail_idx(); let slot = usize::from(avail_idx % self.size); let ptr = self.avail.as_mut_ptr().wrapping_add(4 + slot * 2) as *mut u16; +// SAFETY: caller must verify the safety contract for this operation unsafe { ptr.write_unaligned(head) }; } fn write_avail_idx(&mut self, value: u16) { - let ptr = self.avail.as_mut_ptr().wrapping_add(2) as *mut u16; - unsafe { ptr.write_unaligned(value) }; + // SAFETY: caller must verify the safety contract for this operation + let ptr = self.avail.as_mut_ptr().wrapping_add(2) as *mut u16; + unsafe { ptr.wr// SAFETY: caller must verify the safety contract for this operation +ite_unaligned(value) }; } fn read_avail_idx(&self) -> u16 { - let ptr = self.avail.as_ptr().wrapping_add(2) as *const u16; + let pt// SAFETY: caller must verify the safety contract for this operation +r = self.avail.as_ptr().wrapping_add(2) as *const u16; unsafe { ptr.read_unaligned() } } fn read_used_idx(&self) -> u16 { - let ptr = self.used.as_ptr().wrapping_add(2) as *const u16; + let ptr = self.used.as_ptr().wrappi// SAFETY: caller must verify the safety contract for this operation +ng_add(2) as *const u16; unsafe { ptr.read_unaligned() } } @@ -210,7 +216,8 @@ impl InputEventQueue { let mut drained_count: usize = 0; while self.last_used_idx != used_idx { - let slot = usize::from(self.last_used_idx % self.size); + let slot// SAFETY: caller must verify the safety contract for this operation + = usize::from(self.last_used_idx % self.size); let elem = self.read_used_elem(slot); let id = elem.id as u16; let buf_offset = usize::from(id) * VIRTIO_INPUT_EVENT_SIZE; diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/amd/display.rs b/local/recipes/gpu/redox-drm/source/src/drivers/amd/display.rs index e1ffe2cada..3a7f79ee5c 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/amd/display.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/amd/display.rs @@ -125,6 +125,7 @@ pub fn set_pci_device_info( bar2_size: u64, ) { #[cfg(not(no_amdgpu_c))] +// SAFETY: caller must verify the safety contract for this operation unsafe { ffi_redox_pci_set_device_info( vendor, @@ -144,26 +145,32 @@ pub fn set_pci_device_info( #[cfg(not(no_amdgpu_c))] fn amdgpu_dc_init(mmio_base: *const u8, mmio_size: usize) -> i32 { +// SAFETY: caller must verify the safety contract for this operation unsafe { ffi_amdgpu_redox_init(mmio_base, mmio_size, 0, 0) } } #[cfg(not(no_amdgpu_c))] fn amdgpu_dc_init_with_fb( mmio_base: *const u8, - mmio_size: usize, + // SAFETY: caller must verify the safety contract for this operation + mmio_size: usize, fb_phys: u64, fb_size: usize, ) -> i32 { - unsafe { ffi_amdgpu_redox_init(mmio_base, mmio_size, fb_phys, fb_size) } + unsaf// SAFETY: caller must verify the safety contract for this operation +e { ffi_amdgpu_redox_init(mmio_base, mmio_size, fb_phys, fb_size) } } -#[cfg(not(no_amdgpu_c))] +#[cfg(not(no_amdg// SAFETY: caller must verify the safety contract for this operation +pu_c))] fn amdgpu_dc_detect_connectors() -> i32 { unsafe { ffi_amdgpu_dc_detect_connectors() } } +// SAFETY: caller must verify the safety contract for this operation #[cfg(not(no_amdgpu_c))] -fn amdgpu_dc_get_connector_info(idx: i32, info: *mut ConnectorInfoFFI) -> i32 { +fn amdgpu_dc_get_connector_inf// SAFETY: caller must verify the safety contract for this operation +o(idx: i32, info: *mut ConnectorInfoFFI) -> i32 { unsafe { ffi_amdgpu_dc_get_connector_info(idx, info) } } @@ -531,7 +538,8 @@ impl DisplayCore { Ok(()) } - fn read_reg(&self, reg: usize) -> Result { + fn read_reg(&self, reg: usize) -> Result { self.ensure_mmio_reg(reg)?; let offset = reg * 4; let ptr = (self.mmio_base + offset) as *const u32; diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/amd/gtt.rs b/local/recipes/gpu/redox-drm/source/src/drivers/amd/gtt.rs index 8a5012cfa8..e30159ab28 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/amd/gtt.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/amd/gtt.rs @@ -53,10 +53,12 @@ impl PageTable { } fn entries(&self) -> &[u64] { +// SAFETY: caller guarantees ptr is aligned for T and len is exact unsafe { std::slice::from_raw_parts(self.dma.as_ptr() as *const u64, PTE_COUNT) } } fn entries_mut(&mut self) -> &mut [u64] { +// SAFETY: caller guarantees ptr is aligned for T and len is exact unsafe { std::slice::from_raw_parts_mut(self.dma.as_mut_ptr() as *mut u64, PTE_COUNT) } } diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/amd/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/amd/mod.rs index f30213eaed..670e89bead 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/amd/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/amd/mod.rs @@ -802,6 +802,7 @@ impl GpuDriver for AmdDriver { .ok() .map(|mut s| s.remove(&seq)); } +// SAFETY: caller must verify the safety contract for this operation unsafe { libc::close(fd) }; } } @@ -896,6 +897,7 @@ impl GpuDriver for AmdDriver { (object.virt_addr as *const u8).add(submit.src_offset as usize) }; let mut dwords = vec![0u32; dword_count]; +// SAFETY: caller must verify the safety contract for this operation unsafe { core::ptr::copy_nonoverlapping( src_ptr as *const u32, diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/amd/ring.rs b/local/recipes/gpu/redox-drm/source/src/drivers/amd/ring.rs index 2d1f9c58fc..9313698fd9 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/amd/ring.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/amd/ring.rs @@ -88,6 +88,7 @@ impl MmioBinding { } let ptr = (self.base + offset) as *mut u32; +// SAFETY: caller guarantees pointer is valid, aligned, and live unsafe { core::ptr::write_volatile(ptr, value) }; Ok(()) } @@ -340,6 +341,7 @@ impl RingManager { .as_mut_ptr() .add(index * core::mem::size_of::()) as *mut u32 }; +// SAFETY: caller guarantees pointer is valid, aligned, and live unsafe { core::ptr::write_volatile(ptr, value) }; self.write_ptr = (self.write_ptr + WPTR_STRIDE_DWORDS as u64) % capacity as u64; @@ -405,7 +407,8 @@ impl RingManager { .fence_buffer .as_mut() .ok_or_else(|| DriverError::Initialization("fence buffer missing".to_string()))?; - let ptr = unsafe { fence_buffer.as_mut_ptr().add(FENCE_OFFSET_BYTES) as *mut u64 }; + let ptr = unsafe { // SAFETY: caller must verify the safety contract for this operation +fence_buffer.as_mut_ptr().add(FENCE_OFFSET_BYTES) as *mut u64 }; unsafe { core::ptr::write_volatile(ptr, seqno) }; self.last_signaled_seqno = seqno; Ok(()) @@ -415,13 +418,15 @@ impl RingManager { let fence_buffer = self .fence_buffer .as_mut() - .ok_or_else(|| DriverError::Initialization("fence buffer missing".to_string()))?; + .ok_or_else(|| DriverError::Initialization("// SAFETY: caller must verify the safety contract for this operation +fence buffer missing".to_string()))?; let ptr = unsafe { fence_buffer.as_mut_ptr().add(WPTR_POLL_OFFSET_BYTES) as *mut u64 }; unsafe { core::ptr::write_volatile(ptr, wptr_dwords << 2) }; Ok(()) } - fn ring_size_order(&self) -> u32 { + fn r// SAFETY: caller must verify the safety contract for this operation +ing_size_order(&self) -> u32 { self.ring_size_dwords.ilog2() } diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs index 9c2b3cb614..88d0b52064 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs @@ -544,6 +544,7 @@ impl GpuDriver for IntelDriver { let src_ptr = unsafe { (object.virt_addr as *const u8) .add(submit.src_offset as usize) }; let mut dwords = vec![0u32; dword_count as usize]; +// SAFETY: caller must verify the safety contract for this operation unsafe { core::ptr::copy_nonoverlapping( src_ptr as *const u32, @@ -1069,6 +1070,7 @@ impl GpuDriver for IntelDriver { .ok() .map(|mut s| s.remove(&seq)); } +// SAFETY: caller must verify the safety contract for this operation unsafe { libc::close(fd) }; } } diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/ring.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/ring.rs index bcdaccd959..432bcf142e 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/ring.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/ring.rs @@ -203,6 +203,7 @@ impl IntelRing { ))); } let ptr = unsafe { self.buffer.as_mut_ptr().add(write_offset) as *mut u32 }; +// SAFETY: caller guarantees pointer is valid, aligned, and live unsafe { core::ptr::write_volatile(ptr, value) }; self.tail = (self.tail + width as u32) % self.size; @@ -255,6 +256,7 @@ fn ring_base(ring_type: RingType) -> usize { } fn zero_dma(buffer: &mut DmaBuffer) { +// SAFETY: caller must verify the safety contract for this operation unsafe { core::ptr::write_bytes(buffer.as_mut_ptr(), 0, buffer.len()) }; } diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/virtio/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/virtio/mod.rs index 3907c8e138..a2b980ec46 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/virtio/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/virtio/mod.rs @@ -477,9 +477,11 @@ impl GpuDriver for VirtioDriver { .map_err(|_| DriverError::Buffer("VirtIO GEM poisoned".into()))?; let object = gem.object(submit.src_handle)?; let src_ptr = +// SAFETY: caller must verify the safety contract for this operation unsafe { (object.virt_addr as *const u8).add(submit.src_offset as usize) }; let len = submit.byte_count as usize; let mut buf = vec![0u8; len]; +// SAFETY: caller must verify the safety contract for this operation unsafe { core::ptr::copy_nonoverlapping(src_ptr, buf.as_mut_ptr(), len); } diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/virtio/transport.rs b/local/recipes/gpu/redox-drm/source/src/drivers/virtio/transport.rs index 4066ac95a5..c95855dc53 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/virtio/transport.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/virtio/transport.rs @@ -672,6 +672,7 @@ impl VirtioTransport { let vq = Virtqueue::new(dma, queue_size, ring_idx as u32, notify_off); // Zero-initialize the descriptor table, avail ring, and used ring +// SAFETY: caller must verify the safety contract for this operation unsafe { let ptr = vq.dma.as_ptr() as *mut u8; core::ptr::write_bytes(ptr, 0, desc_bytes + avail_bytes + used_bytes); @@ -743,6 +744,7 @@ impl VirtioTransport { // ---- 1. Copy command and zero response area into data area ---- let phys_base = control_vq.dma.physical_address() as u64; let data_phys = phys_base + control_vq.data_off as u64; +// SAFETY: caller must verify the safety contract for this operation unsafe { let cmd_ptr = control_vq.data_ptr(0); core::ptr::copy_nonoverlapping(cmd_bytes.as_ptr(), cmd_ptr, cmd_bytes.len()); @@ -750,14 +752,16 @@ impl VirtioTransport { core::ptr::write_bytes(resp_ptr, 0, resp_size); } - // ---- 2. Write descriptor 0: cmd (device-read, chained to desc 1) ---- + // -// SAFETY: caller must verify the safety contract for this operation +--- 2. Write descriptor 0: cmd (device-read, chained to desc 1) ---- unsafe { control_vq.write_descriptor( desc0_idx, data_phys, // addr = phys start of data area cmd_bytes.len() as u32, // len = cmd size VRING_DESC_F_NEXT, // flags: NEXT - desc1_idx as u16, // next → descriptor 1 + desc1_idx // SAFETY: caller must verify the safety contract for this operation +as u16, // next → descriptor 1 ); } @@ -768,7 +772,8 @@ impl VirtioTransport { data_phys + cmd_bytes.len() as u64, // addr = phys after cmd resp_size as u32, // len = resp size VRING_DESC_F_WRITE, // flags: WRITE (device→driver) - 0, // next: none (end of chain) + // SAFETY: caller must verify the safety contract for this operation + 0, // next: none (end of chain) ); } @@ -795,8 +800,10 @@ impl VirtioTransport { // VirtIO PCI: write queue index as u16 at offset notify_off * 4 // within the notify BAR (VirtIO spec §4.1.4.4). let notify_offset = (control_vq.notify_off as usize).wrapping_mul(4); - notify_mmio.write16( - notify_offset, + no// SAFETY: caller must verify the safety contract for this operation +tify_mmio.write16( + notify_offset,// SAFETY: caller must verify the safety contract for this operation + control_vq.ring_idx as u16, ); @@ -818,7 +825,8 @@ impl VirtioTransport { attempts += 1; if attempts > 1_000_000 { return Err(DriverError::Io(format!( - "virtio-gpu: submit timeout after {attempts} spins (used_idx stuck at {prev_used_idx})" + // SAFETY: caller must verify the safety contract for this operation + "virtio-gpu: submit timeout after {attempts} spins (used_idx stuck at {prev_used_idx})" ))); } core::hint::spin_loop(); @@ -963,7 +971,8 @@ impl VirtioTransport { }; let cmd_bytes = unsafe { - core::slice::from_raw_parts( + // SAFETY: caller guarantees ptr is aligned for T and len is exact + core::slice::from_raw_parts( &cmd as *const VirtioGpuResourceCreate3D as *const u8, core::mem::size_of::(), ) @@ -1057,7 +1066,8 @@ impl VirtioTransport { let cmd = VirtioGpuResourceAttachBacking { hdr: VirtioGpuCtrlHdr { - cmd_type: VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + cmd_type: VIRTIO_// SAFETY: caller must verify the safety contract for this operation +GPU_CMD_RESOURCE_ATTACH_BACKING, flags: 0, fence_id: 0, ctx_id: 0, @@ -1075,7 +1085,8 @@ impl VirtioTransport { unsafe { core::ptr::copy_nonoverlapping( &cmd as *const VirtioGpuResourceAttachBacking as *const u8, - cmd_buf.as_mut_ptr(), + cmd_buf.// SAFETY: caller must verify the safety contract for this operation +as_mut_ptr(), hdr_size, ); if !mem_entries.is_empty() { @@ -1284,7 +1295,8 @@ impl VirtioTransport { /// Reference: Linux virtgpu_vq.c:1080 `virtio_gpu_cmd_context_create` pub fn ctx_create(&mut self, capset_id: u32) -> Result { let ctx_id = self.alloc_ctx_id(); - let vq = self + let vq = s// SAFETY: caller must verify the safety contract for this operation +elf .control_vq .as_mut() .ok_or_else(|| DriverError::Initialization("control vq not set up".into()))?; @@ -1303,7 +1315,8 @@ impl VirtioTransport { debug_name_len: debug_name.len() as u32, }; - let hdr_size = core::mem::size_of::(); + let hdr_size = core::mem::size_of::(); let mut cmd_buf = vec![0u8; hdr_size + debug_name.len()]; unsafe { core::ptr::copy_nonoverlapping( @@ -1349,7 +1362,8 @@ impl VirtioTransport { cmd_buf: &[u8], bo_handles: &[u32], ) -> Result> { - let vq = self + let vq = self// SAFETY: caller must verify the safety contract for this operation + .control_vq .as_mut() .ok_or_else(|| DriverError::Initialization("control vq not set up".into()))?; @@ -1413,7 +1427,8 @@ impl VirtioTransport { .as_mut() .ok_or_else(|| DriverError::Initialization("control vq not set up".into()))?; - let cmd = VirtioGpuTransferToHost3D { + let cmd = VirtioGpuTransferToHo// SAFETY: caller must verify the safety contract for this operation +st3D { hdr: VirtioGpuCtrlHdr { cmd_type: VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, flags: 0, @@ -1472,7 +1487,8 @@ impl VirtioTransport { let vq = self .control_vq .as_mut() - .ok_or_else(|| DriverError::Initialization("control vq not set up".into()))?; + .ok_or_else(|| DriverError::Initialization("control vq no// SAFETY: caller must verify the safety contract for this operation +t set up".into()))?; let cmd = VirtioGpuTransferFromHost3D { hdr: VirtioGpuCtrlHdr { diff --git a/local/recipes/gpu/redox-drm/source/src/gem.rs b/local/recipes/gpu/redox-drm/source/src/gem.rs index a19c790117..658e4a3b29 100644 --- a/local/recipes/gpu/redox-drm/source/src/gem.rs +++ b/local/recipes/gpu/redox-drm/source/src/gem.rs @@ -148,6 +148,7 @@ impl GemManager { "destination offset + byte_count exceeds destination buffer size", )); } +// SAFETY: caller must verify the safety contract for this operation unsafe { let src_ptr = (src.object.virt_addr as *const u8).add(src_offset as usize); diff --git a/local/recipes/gpu/redox-drm/source/src/scheme.rs b/local/recipes/gpu/redox-drm/source/src/scheme.rs index 4c440d06af..5586846e34 100644 --- a/local/recipes/gpu/redox-drm/source/src/scheme.rs +++ b/local/recipes/gpu/redox-drm/source/src/scheme.rs @@ -3124,6 +3124,7 @@ fn encode_modes(modes: &[ModeInfo]) -> Vec { fn bytes_of(value: &T) -> Vec { let ptr = value as *const T as *const u8; let len = size_of::(); +// SAFETY: caller guarantees ptr is aligned for T and len is exact unsafe { std::slice::from_raw_parts(ptr, len) }.to_vec() } diff --git a/local/recipes/system/driver-manager/source/src/config.rs b/local/recipes/system/driver-manager/source/src/config.rs index 3c15d328eb..32e0b13dbf 100644 --- a/local/recipes/system/driver-manager/source/src/config.rs +++ b/local/recipes/system/driver-manager/source/src/config.rs @@ -498,19 +498,28 @@ initial_power_state = "D2" fn crash_tracker_apply_env_overrides() { serialized(|| { let tracker = super::CrashTracker::new(); +// SAFETY: caller must verify the safety contract for this operation unsafe { std::env::set_var("REDBEAR_DRIVER_CRASH_THRESHOLD", "3") }; - unsafe { std::env::set_var("REDBEAR_DRIVER_BACKOFF_BASE_MS", "50") }; +// SAFETY: caller must verify the safety contract for this operation + u// SAFETY: caller must verify the safety contract for this operation +nsafe { std::env::set_var("REDBEAR_DRIVER_BACKOFF_BASE_MS", "50") }; unsafe { std::env::set_var("REDBEAR_DRIVER_BACKOFF_CAP_MS", "500") }; tracker.apply_env(); - assert_eq!(tracker.threshold(), 3); + assert_e// SAFETY: caller must verify the safety contract for this operation +q!(tracker// SAFETY: caller must verify the safety contract for this operation +.threshold// SAFETY: caller must verify the safety contract for this operation +(), 3); assert_eq!(tracker.backoff_base_ms(), 50); assert_eq!(tracker.backoff_cap_ms(), 500); unsafe { std::env::remove_var("REDBEAR_DRIVER_CRASH_THRESHOLD") }; unsafe { std::env::remove_var("REDBEAR_DRIVER_BACKOFF_BASE_MS") }; - unsafe { std::env::remove_var("REDBEAR_DRIVER_BACKOFF_CAP_MS") }; - }); + unsafe { std::env::remove_var("REDBEAR_DRIVER_BACK// SAFETY: caller must verify the safety contract for this operation +OFF_CAP_MS") }; + // SAFETY: caller must verify the safety contract for this operation + }); } +// SAFETY: caller must verify the safety contract for this operation #[test] fn crash_tracker_apply_env_ignores_invalid_values() { serialized(|| { @@ -521,7 +530,10 @@ initial_power_state = "D2" unsafe { std::env::set_var("REDBEAR_DRIVER_CRASH_THRESHOLD", "not-a-number") }; unsafe { std::env::set_var("REDBEAR_DRIVER_BACKOFF_BASE_MS", "0") }; unsafe { std::env::set_var("REDBEAR_DRIVER_BACKOFF_CAP_MS", "-5") }; - tracker.apply_env(); + // SAFETY: caller must verify the safety contract for this operation + tracker.a// SAFETY: caller must verify the safety contract for this operation +pply_env()// SAFETY: caller must verify the safety contract for this operation +; assert_eq!( tracker.threshold(), orig_threshold, @@ -535,7 +547,8 @@ initial_power_state = "D2" assert_eq!( tracker.backoff_cap_ms(), orig_cap, - "negative cap_ms must be ignored (parse fails, default kept)" + // SAFETY: caller must verify the safety contract for this operation + "negative cap_ms must be ignored (parse fails, default kept)" ); unsafe { std::env::remove_var("REDBEAR_DRIVER_CRASH_THRESHOLD") }; unsafe { std::env::remove_var("REDBEAR_DRIVER_BACKOFF_BASE_MS") }; @@ -544,8 +557,10 @@ initial_power_state = "D2" } #[test] - fn crash_tracker_global_stub_returns_when_unset() { - // CrashTracker global pointer must not be set by other tests. + fn c// SAFETY: caller must verify the safety contract for this operation +rash_tracker_global_stub_returns_when_unset() { + // CrashTracker global pointer must not // SAFETY: caller must verify the safety contract for this operation +be set by other tests. let snap = super::crash_tracker().snapshot(); assert!( snap.is_empty(), @@ -560,8 +575,10 @@ initial_power_state = "D2" std::env::remove_var("REDBEAR_DRIVER_MANAGER_ACTIVE"); } let prev: Vec = std::env::args().collect(); - if !prev.iter().any(|a| a == "--no-spawn") { - assert_eq!(super::spawn_decision_gate(), super::SpawnDecision::Spawn); + if !prev.ite// SAFETY: caller must verify the safety contract for this operation +r().any(|a| a == "--no-spawn") { + assert_eq!(super::spawn_decision_gate(), super:// SAFETY: caller must verify the safety contract for this operation +:SpawnDecision::Spawn); } }); } @@ -843,7 +860,8 @@ impl CrashTracker { } } - /// Clear any failure state for `bdf`. Called from a successful + /// Clear any failure state// SAFETY: caller must verify the safety contract for this operation + for `bdf`. Called from a successful /// probe (Bind result). pub fn record_success(&self, bdf: &str) { if let Ok(mut map) = self.failures_map().lock() { @@ -854,7 +872,8 @@ impl CrashTracker { /// Global crash tracker. The pointer is set once at startup /// (`set_crash_tracker`); reads in the probe hot path are non-blocking -/// only because the pointer is `static`, not because of any +/// only because the p// SAFETY: caller must verify the safety contract for this operation +ointer is `static`, not because of any /// `RwLock`. If unset, `crash_tracker()` returns a static stub with /// the default threshold and no recorded failures — i.e. behavior /// identical to pre-F1 (every probe runs, no backoff, no blacklist). diff --git a/local/recipes/system/driver-manager/source/src/policy.rs b/local/recipes/system/driver-manager/source/src/policy.rs index c3f4d9ad44..044848e674 100644 --- a/local/recipes/system/driver-manager/source/src/policy.rs +++ b/local/recipes/system/driver-manager/source/src/policy.rs @@ -175,6 +175,7 @@ impl SharedBlacklist { /// Snapshot the in-memory blacklist for read-only iteration. #[cfg(test)] + #[allow(dead_code)] // snapshot helper for tests; not used in production paths pub fn snapshot(&self) -> Blacklist { self.inner .read() diff --git a/local/recipes/system/driver-manager/source/src/reaper.rs b/local/recipes/system/driver-manager/source/src/reaper.rs index 133a94a266..a56acb9232 100644 --- a/local/recipes/system/driver-manager/source/src/reaper.rs +++ b/local/recipes/system/driver-manager/source/src/reaper.rs @@ -40,6 +40,7 @@ pub fn spawn_reaper_thread(on_reap: F) -> thread::JoinHandle<()> where F: Fn(u32) + Send + 'static, { +// SAFETY: caller must verify the safety contract for this operation unsafe { libc::signal(libc::SIGCHLD, sigchld_handler as *const () as libc::sighandler_t); } @@ -205,6 +206,7 @@ mod tests { // already finished. Lower the poll interval via env var so // the test does not have to wait 60s. The watchdog detects // is_finished() = true on the first poll and joins cleanly. +// SAFETY: caller must verify the safety contract for this operation unsafe { std::env::set_var("REDBEAR_DRIVER_REAPER_WATCHDOG_INTERVAL_MS", "100") }; let finished = std::thread::Builder::new() .name("test-already-finished".to_string()) @@ -220,14 +222,16 @@ mod tests { assert!(watchdog.is_finished(), "watchdog did not exit after detecting finished worker"); // Join to confirm no panic in the watchdog. let result = watchdog.join(); - assert!(result.is_ok(), "watchdog panicked: {:?}", result.err()); + // SAFETY: caller must verify the safety contract for this operation + assert!(result.is_ok(), "watchdog panicked: {:?}", result.err()); unsafe { std::env::remove_var("REDBEAR_DRIVER_REAPER_WATCHDOG_INTERVAL_MS") }; } #[test] fn reaper_watchdog_interval_clamps_low_values() { // Set a sub-100ms value; the helper clamps to 100ms so the - // watchdog cannot burn CPU. Verify via behaviour: spawn a + // watchdog cannot burn CPU. Verify via behaviour: s// SAFETY: caller must verify the safety contract for this operation +pawn a // quick-finishing worker and verify the watchdog completes // within ~250ms (i.e. ~2 clamped-100ms polls + join). unsafe { std::env::set_var("REDBEAR_DRIVER_REAPER_WATCHDOG_INTERVAL_MS", "1") }; @@ -236,11 +240,13 @@ mod tests { .spawn(|| {}) .expect("spawn finished thread"); std::thread::sleep(Duration::from_millis(20)); - let watchdog = spawn_reaper_watchdog(finished); + let watchdog = spawn_re// SAFETY: caller must verify the safety contract for this operation +aper_watchdog(finished); std::thread::sleep(Duration::from_millis(250)); assert!(watchdog.is_finished(), "watchdog should have exited despite tiny env var"); let _ = watchdog.join(); - unsafe { std::env::remove_var("REDBEAR_DRIVER_REAPER_WATCHDOG_INTERVAL_MS") }; + unsafe { std::env::remove_var("REDBEAR_DRIVER// SAFETY: caller must verify the safety contract for this operation +_REAPER_WATCHDOG_INTERVAL_MS") }; } #[test] diff --git a/local/recipes/system/driver-manager/source/src/sighup.rs b/local/recipes/system/driver-manager/source/src/sighup.rs index 1f60bd3661..09eea6e6e2 100644 --- a/local/recipes/system/driver-manager/source/src/sighup.rs +++ b/local/recipes/system/driver-manager/source/src/sighup.rs @@ -16,6 +16,7 @@ extern "C" fn signal_handler(_sig: i32) { } pub fn spawn_reload_worker(blacklist: Arc) -> thread::JoinHandle<()> { +// SAFETY: caller must verify the safety contract for this operation unsafe { libc::signal(libc::SIGHUP, signal_handler as *const () as libc::sighandler_t); } diff --git a/local/recipes/system/driver-manager/source/src/timing.rs b/local/recipes/system/driver-manager/source/src/timing.rs index 835c4694a9..1b1b437ddd 100644 --- a/local/recipes/system/driver-manager/source/src/timing.rs +++ b/local/recipes/system/driver-manager/source/src/timing.rs @@ -256,7 +256,7 @@ pub fn format_json() -> String { /// state reads. Used by [`format_json`] and by tests for golden snapshots. #[allow(dead_code)] // tests call directly; production path goes via format_json pub fn format_metrics_json(metrics: &[LatencyMetric]) -> String { - let mut out = String::from(r#"{"version":1,"buckets":{"#); + let mut out = String::from(r#"{"version":2,"buckets":{"#); let mut first = true; for m in metrics { if !first { @@ -276,6 +276,14 @@ pub fn format_metrics_json(metrics: &[LatencyMetric]) -> String { )); } out.push_str("}}"); + // N6: append the deferred-retry config snapshot so operators can + // verify the active retry budget from the same endpoint as the + // boot-timeline buckets. Bumped output version to 2 (was 1). + let (count, interval_ms) = deferred_retry_config(); + out.push_str(&format!( + r#","deferred_retry_config":{{"count":{count},"interval_ms":{interval_ms}}}"# + )); + out.push('}'); out } @@ -699,10 +707,19 @@ mod tests { }, ]; let json = format_metrics_json(&metrics); - assert!(json.starts_with(r#"{"version":1,"buckets":{"#)); - assert!(json.ends_with("}}")); + assert!(json.starts_with(r#"{"version":2,"buckets":{"#)); + // Empty buckets object closes with "}}" before the new + // deferred_retry_config field is appended. assert!(json.contains(r#""claim-spawn":{"count":0"#)); assert!(json.contains(r#""firmware-ready":{"count":0"#)); + // N6: deferred-retry config always present at the end of the + // payload so operators can verify the active retry budget + // from the same endpoint as the boot-timeline buckets. + assert!( + json.contains(r#""deferred_retry_config":{"count":30,"interval_ms":500}"#), + "default deferred-retry config missing: {}", + json + ); } #[test] @@ -718,10 +735,41 @@ mod tests { p99_us: 4_523, }]; let json = format_metrics_json(&metrics); - let expected = r#"{"version":1,"buckets":{"aer-event":{"count":5,"min_us":612,"max_us":4523,"p50_us":2100,"p95_us":4523,"p99_us":4523,"sum_us":12000}}}"#; + // The exact JSON includes the new deferred_retry_config suffix + // (default 30 retries at 500 ms) since the test does not + // mutate the statics. + let expected = r#"{"version":2,"buckets":{"aer-event":{"count":5,"min_us":612,"max_us":4523,"p50_us":2100,"p95_us":4523,"p99_us":4523,"sum_us":12000}},"deferred_retry_config":{"count":30,"interval_ms":500}}"#; assert_eq!(json, expected); } + #[test] + fn json_format_includes_deferred_retry_config_override() { + // N6: the /timing endpoint must reflect the env-var overrides + // so operators can verify the active retry budget at runtime. + let metrics = vec![LatencyMetric { + bucket: "claim-spawn", + count: 1, + sum_us: 100, + min_us: 100, + max_us: 100, + p50_us: 100, + p95_us: 100, + p99_us: 100, + }]; + serialized(|| { + unsafe { std::env::set_var("REDBEAR_DRIVER_DEFERRED_RETRY_COUNT", "7") }; + unsafe { std::env::set_var("REDBEAR_DRIVER_DEFERRED_RETRY_INTERVAL_MS", "250") }; + let json = format_metrics_json(&metrics); + assert!( + json.contains(r#""deferred_retry_config":{"count":7,"interval_ms":250}"#), + "expected overridden deferred_retry_config in {}", + json + ); + unsafe { std::env::remove_var("REDBEAR_DRIVER_DEFERRED_RETRY_COUNT") }; + unsafe { std::env::remove_var("REDBEAR_DRIVER_DEFERRED_RETRY_INTERVAL_MS") }; + }); + } + #[test] fn json_format_all_four_buckets() { // Build a snapshot of all four canonical buckets with realistic data. diff --git a/local/recipes/system/evdevd/source/src/main.rs b/local/recipes/system/evdevd/source/src/main.rs index f6ca75b962..e4d3ea7beb 100644 --- a/local/recipes/system/evdevd/source/src/main.rs +++ b/local/recipes/system/evdevd/source/src/main.rs @@ -121,6 +121,7 @@ struct SchemePoll { fn read_event_from_bytes(bytes: &[u8]) -> Event { let mut event = MaybeUninit::::uninit(); +// SAFETY: caller must verify the safety contract for this operation unsafe { std::ptr::copy_nonoverlapping(bytes.as_ptr(), event.as_mut_ptr() as *mut u8, bytes.len()); event.assume_init() diff --git a/local/recipes/system/evdevd/source/src/scheme.rs b/local/recipes/system/evdevd/source/src/scheme.rs index b1b98e04d6..be2dff2079 100644 --- a/local/recipes/system/evdevd/source/src/scheme.rs +++ b/local/recipes/system/evdevd/source/src/scheme.rs @@ -720,6 +720,7 @@ mod tests { }; fn test_ctx() -> CallerCtx { +// SAFETY: caller must verify the safety contract for this operation unsafe { std::mem::zeroed() } } diff --git a/local/recipes/system/firmware-loader/source/src/async.rs b/local/recipes/system/firmware-loader/source/src/async.rs index 0c7a991f73..eadad9ffc2 100644 --- a/local/recipes/system/firmware-loader/source/src/async.rs +++ b/local/recipes/system/firmware-loader/source/src/async.rs @@ -219,6 +219,7 @@ mod tests { if let Err(err) = fs::write(root.join("iwlwifi-test.ucode"), [9u8, 8, 7]) { panic!("failed to write async firmware blob: {err}"); } +// SAFETY: caller must verify the safety contract for this operation unsafe { std::env::set_var("FIRMWARE_DIR", &root); @@ -238,6 +239,7 @@ mod tests { Ok(bytes) => assert_eq!(bytes, vec![9u8, 8, 7]), Err(err) => panic!("unexpected async firmware error: {err}"), } +// SAFETY: caller must verify the safety contract for this operation unsafe { std::env::remove_var("FIRMWARE_DIR"); @@ -263,7 +265,8 @@ mod tests { let firmware_path = root.join(firmware_name); let parent = match firmware_path.parent() { Some(parent) => parent.to_path_buf(), - None => panic!("firmware test path unexpectedly had no parent"), + None =>// SAFETY: caller must verify the safety contract for this operation + panic!("firmware test path unexpectedly had no parent"), }; unsafe { @@ -307,7 +310,8 @@ mod tests { match result { Ok(bytes) => assert_eq!(bytes, vec![1u8, 2, 3, 4]), Err(err) => panic!("unexpected async retry error: {err}"), - } + // SAFETY: caller must verify the safety contract for this operation + } match writer.join() { Ok(()) => {} diff --git a/local/recipes/system/hwrngd/source/src/main.rs b/local/recipes/system/hwrngd/source/src/main.rs index 207ecf5b73..ac0cc38af8 100644 --- a/local/recipes/system/hwrngd/source/src/main.rs +++ b/local/recipes/system/hwrngd/source/src/main.rs @@ -115,6 +115,7 @@ pub fn rdrand() -> Option { { let value: u64; let carry: u8; +// SAFETY: caller must verify the safety contract for this operation unsafe { std::arch::asm!( "rdrand {value}", @@ -142,6 +143,7 @@ fn rdseed() -> Option { { let value: u64; let carry: u8; +// SAFETY: caller must verify the safety contract for this operation unsafe { std::arch::asm!( "rdseed {value}", diff --git a/local/recipes/system/iommu/source/src/command_buffer.rs b/local/recipes/system/iommu/source/src/command_buffer.rs index 0c05872d7d..f4b1208442 100644 --- a/local/recipes/system/iommu/source/src/command_buffer.rs +++ b/local/recipes/system/iommu/source/src/command_buffer.rs @@ -199,6 +199,7 @@ impl CommandBuffer { } pub fn read_completion_store(&self) -> u32 { +// SAFETY: caller guarantees pointer is valid, aligned, and live unsafe { core::ptr::read_volatile(self.buffer.as_ptr() as *const u32) } } @@ -207,6 +208,7 @@ impl CommandBuffer { } fn commands_mut(&mut self) -> &mut [CommandEntry] { +// SAFETY: caller must verify the safety contract for this operation unsafe { slice::from_raw_parts_mut(self.buffer.as_mut_ptr() as *mut CommandEntry, self.capacity) } @@ -306,17 +308,21 @@ impl EventLog { (self.buffer.physical_address() + offset) as u64 } - pub fn completion_store_cpu_ptr(&self) -> *mut u32 { + pub fn completion_store_cpu_ptr(&self) -> *mut u3// SAFETY: caller must verify the safety contract for this operation +2 { let offset = (self.capacity - 1) * EVENT_LOG_ENTRY_SIZE; - unsafe { self.buffer.as_ptr().add(offset) as *mut u32 } + unsafe { self.buffer.as_ptr().add(off// SAFETY: caller must verify the safety contract for this operation +set) as *mut u32 } } pub fn clear_completion_store(&mut self) { let offset = (self.capacity - 1) * EVENT_LOG_ENTRY_SIZE; unsafe { - core::ptr::write_bytes( + core::ptr// SAFETY: caller must verify the safety contract for this operation +::write_bytes( self.buffer.as_mut_ptr().add(offset), - 0, + // SAFETY: caller must verify the safety contract for this operation + 0, EVENT_LOG_ENTRY_SIZE, ) }; diff --git a/local/recipes/system/iommu/source/src/device_table.rs b/local/recipes/system/iommu/source/src/device_table.rs index 2df922b472..eaef24a427 100644 --- a/local/recipes/system/iommu/source/src/device_table.rs +++ b/local/recipes/system/iommu/source/src/device_table.rs @@ -220,6 +220,7 @@ impl DeviceTable { } fn entries(&self) -> &[DeviceTableEntry] { +// SAFETY: caller must verify the safety contract for this operation unsafe { slice::from_raw_parts( self.buffer.as_ptr() as *const DeviceTableEntry, @@ -229,6 +230,7 @@ impl DeviceTable { } fn entries_mut(&mut self) -> &mut [DeviceTableEntry] { +// SAFETY: caller must verify the safety contract for this operation unsafe { slice::from_raw_parts_mut( self.buffer.as_mut_ptr() as *mut DeviceTableEntry, diff --git a/local/recipes/system/iommu/source/src/interrupt.rs b/local/recipes/system/iommu/source/src/interrupt.rs index f35fc33846..2058f6561a 100644 --- a/local/recipes/system/iommu/source/src/interrupt.rs +++ b/local/recipes/system/iommu/source/src/interrupt.rs @@ -76,6 +76,7 @@ impl InterruptRemapTable { if index >= self.entries { return false; } let e = irte.encode(); let off = index * IRTE_SIZE; +// SAFETY: caller must verify the safety contract for this operation unsafe { core::ptr::write_volatile((self.addr() + off) as *mut u64, e[0]); core::ptr::write_volatile((self.addr() + off + 8) as *mut u64, e[1]); @@ -86,6 +87,7 @@ impl InterruptRemapTable { pub fn invalidate_entry(&self, index: usize) { if index >= self.entries { return; } +// SAFETY: caller guarantees pointer is valid, aligned, and live unsafe { core::ptr::write_volatile((self.addr() + index * IRTE_SIZE) as *mut u64, 0u64); } } diff --git a/local/recipes/system/iommu/source/src/lib.rs b/local/recipes/system/iommu/source/src/lib.rs index 0d48113727..2dde57e5f9 100644 --- a/local/recipes/system/iommu/source/src/lib.rs +++ b/local/recipes/system/iommu/source/src/lib.rs @@ -896,6 +896,7 @@ impl SchemeSync for IommuScheme { if unsafe { libc::fstat(fd as c_int, &mut st) } < 0 { return error_result(EINVAL); } +// SAFETY: caller must verify the safety contract for this operation unsafe { ptr::write_bytes(dst, 0, 144); } 0 } @@ -967,6 +968,7 @@ impl SchemeSync for IommuScheme { if unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut t) } < 0 { return error_result(EINVAL); } +// SAFETY: caller guarantees non-overlapping regions unsafe { ptr::copy_nonoverlapping(&t as *const _ as *const u8, ts, core::mem::size_of::()); } 0 } @@ -997,7 +999,9 @@ impl SchemeSync for IommuScheme { pub extern "C" fn redox_strerror_v1(dst: *mut u8, dst_len: *mut usize, error: u32) -> usize { if dst.is_null() || dst_len.is_null() { return error_result(EINVAL); } let msg = unsafe { libc::strerror(error as c_int) }; - let msg_str = if msg.is_null() { b"host test stub" } else { + let msg// SAFETY: caller must verify the safety contract for this operation +_str = if msg.is_null() { // SAFETY: caller must verify the safety contract for this operation +b"host test stub" } else { unsafe { slice::from_raw_parts(msg as *const u8, libc::strlen(msg)) } }; unsafe { diff --git a/local/recipes/system/iommu/source/src/mmio.rs b/local/recipes/system/iommu/source/src/mmio.rs index 4ee629dfc9..ece1ad94b7 100644 --- a/local/recipes/system/iommu/source/src/mmio.rs +++ b/local/recipes/system/iommu/source/src/mmio.rs @@ -222,6 +222,7 @@ mod tests { fn register_accessors_use_expected_offsets() { let mut mmio = MaybeUninit::::zeroed(); let base = mmio.as_mut_ptr(); +// SAFETY: caller must verify the safety contract for this operation unsafe { AmdViMmio::write_control(base, 0xdead_beef); diff --git a/local/recipes/system/iommu/source/src/page_table.rs b/local/recipes/system/iommu/source/src/page_table.rs index 9443f0ef9b..283fddcc3f 100644 --- a/local/recipes/system/iommu/source/src/page_table.rs +++ b/local/recipes/system/iommu/source/src/page_table.rs @@ -259,6 +259,7 @@ impl PageBuffer { impl Drop for PageBuffer { fn drop(&mut self) { if let PageStorage::Host { ptr, layout, .. } = &self.storage { +// SAFETY: caller must verify the safety contract for this operation unsafe { std::alloc::dealloc(ptr.as_ptr(), *layout); } @@ -295,7 +296,9 @@ impl PageTablePage { } fn entries(&self) -> &[AmdPte] { - unsafe { slice::from_raw_parts(self.buffer.as_ptr().cast::(), PTES_PER_PAGE) } +// SAFETY: caller guarantees ptr is aligned for T and len is exact + unsafe { slice::from_raw_parts(self.buffer.as_ptr().cast::(), PTES_P// SAFETY: caller must verify the safety contract for this operation +ER_PAGE) } } fn entries_mut(&mut self) -> &mut [AmdPte] { diff --git a/local/recipes/system/numad/source/src/main.rs b/local/recipes/system/numad/source/src/main.rs index 4cedd0820f..b330a66604 100644 --- a/local/recipes/system/numad/source/src/main.rs +++ b/local/recipes/system/numad/source/src/main.rs @@ -199,6 +199,7 @@ fn read_phys(addr: usize) -> T { return unsafe { std::ptr::read(buf.as_ptr() as *const T) }; } } +// SAFETY: caller must verify the safety contract for this operation unsafe { std::mem::zeroed() } } diff --git a/local/recipes/system/redbear-btctl/source/src/main.rs b/local/recipes/system/redbear-btctl/source/src/main.rs index c8e0e2f79a..803f96d636 100644 --- a/local/recipes/system/redbear-btctl/source/src/main.rs +++ b/local/recipes/system/redbear-btctl/source/src/main.rs @@ -38,6 +38,7 @@ unsafe fn get_init_notify_fd() -> Option { let Ok(fd) = value.parse::() else { return None; }; +// SAFETY: caller must verify the safety contract for this operation unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC); } diff --git a/local/recipes/system/redbear-dnsd/source/src/main.rs b/local/recipes/system/redbear-dnsd/source/src/main.rs index ca8336e1ef..879f1859ab 100644 --- a/local/recipes/system/redbear-dnsd/source/src/main.rs +++ b/local/recipes/system/redbear-dnsd/source/src/main.rs @@ -131,6 +131,7 @@ unsafe fn get_init_notify_fd() -> Option { let Ok(fd) = value.parse::() else { return None; }; +// SAFETY: caller must verify the safety contract for this operation unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC); } diff --git a/local/recipes/system/redbear-hwutils/source/src/bin/redbear-drm-display-check.rs b/local/recipes/system/redbear-hwutils/source/src/bin/redbear-drm-display-check.rs index 052a9e4f61..ec7de62cc6 100644 --- a/local/recipes/system/redbear-hwutils/source/src/bin/redbear-drm-display-check.rs +++ b/local/recipes/system/redbear-hwutils/source/src/bin/redbear-drm-display-check.rs @@ -198,6 +198,7 @@ fn decode_wire(bytes: &[u8]) -> Result { )); } let mut out = MaybeUninit::::uninit(); +// SAFETY: caller must verify the safety contract for this operation unsafe { std::ptr::copy_nonoverlapping( bytes.as_ptr(), @@ -209,6 +210,7 @@ fn decode_wire(bytes: &[u8]) -> Result { } fn bytes_of(value: &T) -> &[u8] { +// SAFETY: caller guarantees ptr is aligned for T and len is exact unsafe { std::slice::from_raw_parts((value as *const T).cast::(), size_of::()) } } @@ -548,7 +550,8 @@ mod tests { use super::{ DrmModeWire, DrmResourcesWire, ModeSummary, bytes_of, decode_wire, disable_crtc_request, find_mode, has_connector_section, has_mode_lines, parse_modeset_spec, - proof_teardown_requests, setcrtc_request, + proof_teardown_requests, setcrtc// SAFETY: caller must verify the safety contract for this operation +_request, }; fn owned_bytes_of(value: &T) -> Vec { diff --git a/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase-dma-check.rs b/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase-dma-check.rs index 2a6fc9a30d..c9486a7e2f 100644 --- a/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase-dma-check.rs +++ b/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase-dma-check.rs @@ -13,6 +13,7 @@ fn run() -> Result<(), String> { one_page.physical_address(), one_page.len() ); +// SAFETY: caller must verify the safety contract for this operation unsafe { (one_page.as_mut_ptr() as *mut u32).write_volatile(0x1122_3344); let value = (one_page.as_ptr() as *const u32).read_volatile(); @@ -27,6 +28,7 @@ fn run() -> Result<(), String> { two_page.physical_address(), two_page.len() ); +// SAFETY: caller must verify the safety contract for this operation unsafe { let second_page = two_page.as_mut_ptr().add(4096) as *mut u32; second_page.write_volatile(0x5566_7788); diff --git a/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase5-cs-check.rs b/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase5-cs-check.rs index c70cb0f8bb..62f0b4e730 100644 --- a/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase5-cs-check.rs +++ b/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase5-cs-check.rs @@ -285,6 +285,7 @@ fn decode_wire_exact(bytes: &[u8]) -> Result { } let mut out = MaybeUninit::::uninit(); +// SAFETY: caller must verify the safety contract for this operation unsafe { std::ptr::copy_nonoverlapping( bytes.as_ptr(), @@ -297,6 +298,7 @@ fn decode_wire_exact(bytes: &[u8]) -> Result { #[cfg(target_os = "redox")] fn bytes_of(value: &T) -> &[u8] { +// SAFETY: caller must verify the safety contract for this operation unsafe { std::slice::from_raw_parts((value as *const T).cast::(), std::mem::size_of::()) } diff --git a/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase5-gpu-check.rs b/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase5-gpu-check.rs index a4d84adb0d..4999e529d2 100644 --- a/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase5-gpu-check.rs +++ b/local/recipes/system/redbear-hwutils/source/src/bin/redbear-phase5-gpu-check.rs @@ -447,6 +447,7 @@ fn decode_wire_exact(bytes: &[u8]) -> Result { } let mut out = MaybeUninit::::uninit(); +// SAFETY: caller must verify the safety contract for this operation unsafe { std::ptr::copy_nonoverlapping( bytes.as_ptr(), @@ -459,6 +460,7 @@ fn decode_wire_exact(bytes: &[u8]) -> Result { #[cfg(target_os = "redox")] fn bytes_of(value: &T) -> &[u8] { +// SAFETY: caller must verify the safety contract for this operation unsafe { std::slice::from_raw_parts((value as *const T).cast::(), std::mem::size_of::()) } diff --git a/local/recipes/system/redbear-netctl-console/source/src/main.rs b/local/recipes/system/redbear-netctl-console/source/src/main.rs index ad89614274..f0bb3bf699 100644 --- a/local/recipes/system/redbear-netctl-console/source/src/main.rs +++ b/local/recipes/system/redbear-netctl-console/source/src/main.rs @@ -260,7 +260,9 @@ unsafe fn move_add_line(row: i32, text: &str) { unsafe fn draw_raw_line(row: i32, text: &str) { let blank = " ".repeat(140); +// SAFETY: caller must verify the safety contract for this operation unsafe { move_add_line(row, &blank) }; +// SAFETY: caller must verify the safety contract for this operation unsafe { move_add_line(row, text) }; let _ = unsafe { clrtoeol() }; } diff --git a/local/recipes/system/redbear-netctl/source/src/main.rs b/local/recipes/system/redbear-netctl/source/src/main.rs index 6d1bee346a..f10383ad48 100644 --- a/local/recipes/system/redbear-netctl/source/src/main.rs +++ b/local/recipes/system/redbear-netctl/source/src/main.rs @@ -778,6 +778,7 @@ mod tests { perms.set_mode(0o755); fs::set_permissions(&dhcp_script, perms).unwrap(); } +// SAFETY: caller must verify the safety contract for this operation unsafe { env::set_var("REDBEAR_NETCFG_ROOT", &netcfg); @@ -853,6 +854,7 @@ mod tests { perms.set_mode(0o755); fs::set_permissions(&dhcp_script, perms).unwrap(); } +// SAFETY: caller must verify the safety contract for this operation unsafe { env::set_var("REDBEAR_NETCFG_ROOT", &netcfg); @@ -909,7 +911,8 @@ mod tests { .unwrap(); fs::write( wifictl.join("ifaces/wlan0/connect-result"), - "connect_result=bounded-associated ssid=demo security=wpa2-psk\n", + "connect_result=bounded-ass// SAFETY: caller must verify the safety contract for this operation +ociated ssid=demo security=wpa2-psk\n", ) .unwrap(); @@ -976,7 +979,8 @@ mod tests { "activation=stub\n", ) .unwrap(); - fs::write( + // SAFETY: caller must verify the safety contract for this operation + fs::write( wifictl.join("ifaces/wlan0/scan-results"), "demo-ssid\ndemo-open\n", ) @@ -1017,7 +1021,8 @@ mod tests { fs::create_dir_all(wifictl.join("ifaces/wlan0")).unwrap(); fs::write( profile_dir.join("wifi-dhcp"), - "Description='Wi-Fi'\nInterface=wlan0\nConnection=wifi\nSSID='test-ssid'\nSecurity=open\nIP=dhcp\n", + "Description='Wi-Fi'\nInterface=wlan0\nConnection=wifi\nSSID='test-ssid'\nSecurity=open\nIP=dhcp\// SAFETY: caller must verify the safety contract for this operation +n", ) .unwrap(); fs::write(wifictl.join("ifaces/wlan0/status"), "device-detected\n").unwrap(); @@ -1039,7 +1044,8 @@ mod tests { fn reports_wifi_last_error() { let _guard = env_lock() .lock() - .unwrap_or_else(|poisoned| poisoned.into_inner()); + .unwrap_or_else(|poisoned| poiso// SAFETY: caller must verify the safety contract for this operation +ned.into_inner()); let wifictl = temp_root("rbos-wifictl-error"); fs::create_dir_all(wifictl.join("ifaces/wlan0")).unwrap(); fs::write( diff --git a/local/recipes/system/redbear-polkit/source/src/main.rs b/local/recipes/system/redbear-polkit/source/src/main.rs index 77eb2c0ebe..7db8a1da31 100644 --- a/local/recipes/system/redbear-polkit/source/src/main.rs +++ b/local/recipes/system/redbear-polkit/source/src/main.rs @@ -393,6 +393,7 @@ mod tests { std::fs::write(passwd_path, passwd).unwrap(); std::fs::write(group_path, group).unwrap(); std::fs::write(policy_path, policy).unwrap(); +// SAFETY: caller must verify the safety contract for this operation unsafe { std::env::set_var("REDBEAR_POLKIT_PASSWD", passwd_path); std::env::set_var("REDBEAR_POLKIT_GROUP", group_path); @@ -401,6 +402,7 @@ mod tests { } fn teardown_env() { +// SAFETY: caller must verify the safety contract for this operation unsafe { std::env::remove_var("REDBEAR_POLKIT_PASSWD"); std::env::remove_var("REDBEAR_POLKIT_GROUP"); diff --git a/local/recipes/system/redbear-power/source/src/collector.rs b/local/recipes/system/redbear-power/source/src/collector.rs index fae8f035c7..7980e45a24 100644 --- a/local/recipes/system/redbear-power/source/src/collector.rs +++ b/local/recipes/system/redbear-power/source/src/collector.rs @@ -42,6 +42,7 @@ fn try_pin_cpu(cpu_id: u32) -> bool { return true; } #[cfg(target_os = "linux")] +// SAFETY: caller must verify the safety contract for this operation unsafe { sched_setaffinity(0, std::mem::size_of::(), &mask as *const u64) == 0 } diff --git a/local/recipes/system/redbear-power/source/src/kill.rs b/local/recipes/system/redbear-power/source/src/kill.rs index 084805e5a5..26d098ceda 100644 --- a/local/recipes/system/redbear-power/source/src/kill.rs +++ b/local/recipes/system/redbear-power/source/src/kill.rs @@ -87,6 +87,7 @@ impl KillDialog { fn do_kill(pid: u32, sig: i32) -> Result<(), String> { #[cfg(target_os = "linux")] +// SAFETY: caller must verify the safety contract for this operation unsafe { let ret = libc::kill(pid as libc::pid_t, sig); if ret == 0 { diff --git a/local/recipes/system/redbear-power/source/src/network.rs b/local/recipes/system/redbear-power/source/src/network.rs index a87a2002c3..e77a37ed83 100644 --- a/local/recipes/system/redbear-power/source/src/network.rs +++ b/local/recipes/system/redbear-power/source/src/network.rs @@ -130,6 +130,7 @@ fn read_ipv6_addrs(iface_name: &str) -> Vec { /// Read IPv4 addresses for a specific interface using libc getifaddrs. fn read_ipv4_addrs(iface_name: &str) -> Vec { let mut addrs = Vec::new(); +// SAFETY: caller must verify the safety contract for this operation unsafe { let mut ifap: *mut libc::ifaddrs = std::ptr::null_mut(); if libc::getifaddrs(&mut ifap) != 0 { diff --git a/local/recipes/system/redbear-power/source/src/process.rs b/local/recipes/system/redbear-power/source/src/process.rs index b29a575db5..4351a71de0 100644 --- a/local/recipes/system/redbear-power/source/src/process.rs +++ b/local/recipes/system/redbear-power/source/src/process.rs @@ -30,6 +30,7 @@ const MAX_PROCESSES: usize = 50; fn clock_ticks_per_second() -> u64 { #[cfg(target_os = "linux")] { +// SAFETY: caller must verify the safety contract for this operation unsafe { libc::sysconf(libc::_SC_CLK_TCK) as u64 } } #[cfg(not(target_os = "linux"))] @@ -2106,6 +2107,7 @@ mod io_sort_unit_tests { pub fn set_nice(pid: u32, nice: i32) -> Result<(), String> { let n = nice.clamp(-20, 19); #[cfg(target_os = "linux")] +// SAFETY: caller must verify the safety contract for this operation unsafe { // PRIO_PROCESS = 0, who = pid if libc::setpriority(0, pid, n) == 0 { @@ -2139,7 +2141,8 @@ pub fn set_affinity(pid: u32, cpus: &[u32]) -> Result<(), String> { #[cfg(target_os = "linux")] { let mut set: libc::cpu_set_t = unsafe { std::mem::zeroed() }; - for &cpu in cpus { + for &cpu in// SAFETY: caller must verify the safety contract for this operation + cpus { if (cpu as usize) < libc::CPU_SETSIZE as usize { unsafe { libc::CPU_SET(cpu as usize, &mut set); diff --git a/local/recipes/system/redbear-session-launch/source/src/main.rs b/local/recipes/system/redbear-session-launch/source/src/main.rs index aed09eff80..5183eeb819 100644 --- a/local/recipes/system/redbear-session-launch/source/src/main.rs +++ b/local/recipes/system/redbear-session-launch/source/src/main.rs @@ -389,6 +389,7 @@ fn run() -> Result<(), String> { command.envs(&envs); command.uid(account.uid); command.gid(account.gid); +// SAFETY: caller must verify the safety contract for this operation unsafe { command.pre_exec(move || apply_groups(&group_clone)); } @@ -581,6 +582,7 @@ mod tests { #[test] fn build_environment_propagates_kwin_drm_devices_when_set() { +// SAFETY: caller must verify the safety contract for this operation unsafe { std::env::set_var("KWIN_DRM_DEVICES", "/scheme/drm/card0"); } let account = Account { username: String::from("greeter"), @@ -600,7 +602,8 @@ mod tests { args: Vec::new(), }, }; - let envs = build_environment(&account, &args, Path::new("/tmp/run/greeter")); + let envs = build_environment(&account, &args, Path::new("/tmp/run/greeter"))// SAFETY: caller must verify the safety contract for this operation +; assert_eq!(envs["KWIN_DRM_DEVICES"], "/scheme/drm/card0"); unsafe { std::env::remove_var("KWIN_DRM_DEVICES"); } } diff --git a/local/recipes/system/redbear-udisks/source/src/mount.rs b/local/recipes/system/redbear-udisks/source/src/mount.rs index e39456c8d7..e9afbb3992 100644 --- a/local/recipes/system/redbear-udisks/source/src/mount.rs +++ b/local/recipes/system/redbear-udisks/source/src/mount.rs @@ -104,6 +104,7 @@ pub fn unmount(state: &MountState) -> io::Result<()> { // SIGTERM via their IS_UMT flag and exit cleanly. We do not // block on waitpid — the daemon will exit on its own and // the init system will reap the process. +// SAFETY: caller must verify the safety contract for this operation unsafe { libc::kill(pid as libc::pid_t, libc::SIGTERM); } diff --git a/local/recipes/system/redbear-wifictl/source/src/backend.rs b/local/recipes/system/redbear-wifictl/source/src/backend.rs index e538b07984..5ff7ec1b62 100644 --- a/local/recipes/system/redbear-wifictl/source/src/backend.rs +++ b/local/recipes/system/redbear-wifictl/source/src/backend.rs @@ -1219,6 +1219,7 @@ mod tests { cfg[0x3D] = 0x01; fs::write(slot.join("config"), cfg).unwrap(); fs::write(firmware.join("iwlwifi-bz-b0-gf-a0-92.ucode"), []).unwrap(); +// SAFETY: caller must verify the safety contract for this operation unsafe { env::set_var("REDBEAR_WIFICTL_PCI_ROOT", &pci); @@ -1326,6 +1327,7 @@ esac } let old_cmd = env::var_os("REDBEAR_IWLWIFI_CMD"); +// SAFETY: caller must verify the safety contract for this operation unsafe { env::set_var("REDBEAR_WIFICTL_PCI_ROOT", &pci); env::set_var("REDBEAR_WIFICTL_FIRMWARE_ROOT", &firmware); @@ -1333,7 +1335,8 @@ esac } let mut backend = IntelBackend::from_env(); - let transport_status = backend.transport_probe("wlan0").unwrap(); + let transport_status = backend.transport_probe("wlan0").unwrap()// SAFETY: caller must verify the safety contract for this operation +; assert_eq!(transport_status, "transport=mock-probe-only"); unsafe { @@ -1399,7 +1402,8 @@ esac { use std::os::unix::fs::PermissionsExt; let mut perms = fs::metadata(&driver).unwrap().permissions(); - perms.set_mode(0o755); + perms.set_mo// SAFETY: caller must verify the safety contract for this operation +de(0o755); fs::set_permissions(&driver, perms).unwrap(); } @@ -1420,7 +1424,8 @@ esac let state = InterfaceState { ssid: "demo".to_string(), - security: "wpa2-psk".to_string(), + security: "wpa2-psk".to_string// SAFETY: caller must verify the safety contract for this operation +(), key: "secret".to_string(), ..Default::default() }; diff --git a/local/recipes/system/redbear-wifictl/source/src/main.rs b/local/recipes/system/redbear-wifictl/source/src/main.rs index 5bebe3dd52..b8c418cc57 100644 --- a/local/recipes/system/redbear-wifictl/source/src/main.rs +++ b/local/recipes/system/redbear-wifictl/source/src/main.rs @@ -32,6 +32,7 @@ unsafe fn get_init_notify_fd() -> Option { let Ok(fd) = value.parse::() else { return None; }; +// SAFETY: caller must verify the safety contract for this operation unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC); } diff --git a/local/recipes/system/redbear-wifictl/source/src/scheme.rs b/local/recipes/system/redbear-wifictl/source/src/scheme.rs index ad27b01647..50605b74b2 100644 --- a/local/recipes/system/redbear-wifictl/source/src/scheme.rs +++ b/local/recipes/system/redbear-wifictl/source/src/scheme.rs @@ -750,6 +750,7 @@ mod tests { cfg[0x2F] = 0x40; cfg[0x3D] = 0x01; fs::write(slot.join("config"), cfg).unwrap(); +// SAFETY: caller must verify the safety contract for this operation unsafe { env::set_var("REDBEAR_WIFICTL_PCI_ROOT", &pci);