firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

eepy: enforce pointer validity in syscalls

arthomnix 6683eb32 78326692

+90 -41
+6 -3
eepy-sys/src/header.rs
··· 25 25 slot_ptr(id).cast() 26 26 }} 27 27 28 - pub fn slot_of_addr<T>(addr: *const T) -> u8 { 29 - ((addr as usize - XIP_BASE as usize) / SLOT_SIZE) as u8 28 + pub fn slot_of_ptr<T>(ptr: *const T) -> u8 { 29 + slot_of_addr(ptr as usize) 30 30 } 31 31 32 + pub fn slot_of_addr(addr: usize) -> u8 { 33 + ((addr - XIP_BASE as usize) / SLOT_SIZE) as u8 34 + } 32 35 33 36 pub struct Programs { 34 37 id: u8, ··· 222 225 let prog_name = unsafe { (**psh).name() }; 223 226 prog_name == Ok(name) 224 227 }) 225 - .map(slot_of_addr) 228 + .map(slot_of_ptr) 226 229 }
+10 -10
eepy-sys/src/kv_store.rs
··· 16 16 #[repr(C)] 17 17 pub struct ReadKeyArgs { 18 18 pub key_len: usize, 19 - pub key_ptr: *const u8, 19 + pub key_ptr: usize, 20 20 pub buf_len: usize, 21 - pub buf_ptr: *mut u8, 21 + pub buf_ptr: usize, 22 22 } 23 23 24 24 #[repr(C)] 25 25 pub struct WriteKeyArgs { 26 26 pub key_len: usize, 27 - pub key_ptr: *const u8, 27 + pub key_ptr: usize, 28 28 pub value_len: usize, 29 - pub value_ptr: *const u8, 29 + pub value_ptr: usize, 30 30 } 31 31 32 32 #[repr(usize)] ··· 130 130 pub fn get(key: &[u8], buf: &mut [u8]) -> Result<usize, ReadKeyError> { 131 131 let args = ReadKeyArgs { 132 132 key_len: key.len(), 133 - key_ptr: key.as_ptr(), 133 + key_ptr: key.as_ptr().expose_provenance(), 134 134 buf_len: buf.len(), 135 - buf_ptr: buf.as_mut_ptr(), 135 + buf_ptr: buf.as_mut_ptr().expose_provenance(), 136 136 }; 137 137 let mut res: MaybeUninit<SafeResult<usize, ReadKeyError>> = MaybeUninit::uninit(); 138 138 ··· 151 151 pub fn append(key: &[u8], value: &[u8]) -> Result<(), AppendKeyError> { 152 152 let args = WriteKeyArgs { 153 153 key_len: key.len(), 154 - key_ptr: key.as_ptr(), 154 + key_ptr: key.as_ptr().expose_provenance(), 155 155 value_len: value.len(), 156 - value_ptr: value.as_ptr(), 156 + value_ptr: value.as_ptr().expose_provenance(), 157 157 }; 158 158 let mut res: MaybeUninit<SafeResult<(), AppendKeyError>> = MaybeUninit::uninit(); 159 159 ··· 172 172 pub fn put(key: &[u8], value: &[u8]) -> Result<(), PutKeyError> { 173 173 let args = WriteKeyArgs { 174 174 key_len: key.len(), 175 - key_ptr: key.as_ptr(), 175 + key_ptr: key.as_ptr().expose_provenance(), 176 176 value_len: value.len(), 177 - value_ptr: value.as_ptr(), 177 + value_ptr: value.as_ptr().expose_provenance(), 178 178 }; 179 179 let mut res: MaybeUninit<SafeResult<(), PutKeyError>> = MaybeUninit::uninit(); 180 180
+1 -1
eepy/Cargo.toml
··· 1 1 [package] 2 2 name = "eepy" 3 - version = "0.1.1" 3 + version = "0.1.2" 4 4 edition = "2024" 5 5 authors = ["arthomnix"] 6 6 license = "MIT"
+2 -2
eepy/src/exception.rs
··· 8 8 pub r2: usize, 9 9 pub r3: usize, 10 10 pub r12: usize, 11 - pub lr: *const u8, 12 - pub pc: *const u8, 11 + pub lr: usize, 12 + pub pc: usize, 13 13 pub xpsr: usize, 14 14 } 15 15
+2 -2
eepy/src/main.rs
··· 47 47 use crate::gpio_irq::{GLOBAL_EPD_POWER_PIN, GLOBAL_I2C, GLOBAL_SLEEP_PIN, GLOBAL_TOUCH_INT_PIN, GLOBAL_TOUCH_RESET_PIN}; 48 48 use crate::ringbuffer::RingBuffer; 49 49 50 - const SRAM_END: *mut u8 = 0x20042000 as *mut u8; 50 + const SRAM_END: usize = 0x20042000; 51 51 52 52 static CORE1_STACK: Stack<8192> = Stack::new(); 53 53 ··· 84 84 read_serial(); 85 85 } 86 86 87 - info!("eepyOS version {} (c) arthomnix 2025", env!("CARGO_PKG_VERSION")); 87 + info!("eepyOS version {} (c) arthomnix 2026", env!("CARGO_PKG_VERSION")); 88 88 info!("Serial number: {}", unsafe { core::str::from_utf8_unchecked(SERIAL_NUMBER.get().unwrap()) }); 89 89 90 90 let mut sio = Sio::new(pac.SIO);
+24 -1
eepy/src/syscall.rs
··· 10 10 use defmt::trace; 11 11 use eepy_sys::syscall::SyscallNumber; 12 12 use crate::exception::StackFrame; 13 + use crate::SRAM_END; 14 + 15 + const PROGRAM_SRAM_START: usize = 0x20020000; 16 + const FLASH_START: usize = 0x10000000; 17 + const KERNEL_FLASH_END: usize = 0x10020000; 18 + const FLASH_END: usize = 0x11000000; 13 19 14 20 global_asm!(include_str!("syscall.s")); 15 21 ··· 27 33 extern "C" fn handle_syscall(sp: *mut StackFrame, using_psp: bool) { 28 34 // Stack contains R0, R1, R2, R3, R12, LR, ReturnAddress, xPSR 29 35 let stack_values = unsafe { &mut *sp }; 30 - let syscall_num = unsafe { *stack_values.pc.sub(2) }; 36 + let syscall_num = unsafe { *((stack_values.pc - 2) as *const u8) }; 31 37 32 38 trace!("syscall: sp={} imm={:x} {}", sp, syscall_num, stack_values); 33 39 ··· 41 47 Some(SyscallNumber::Flash) => flash::handle_flash(stack_values), 42 48 Some(SyscallNumber::KvStore) => kv_store::handle_kv_store(stack_values), 43 49 None => panic!("illegal syscall"), 50 + } 51 + } 52 + 53 + /// Check that a pointer provided by the program via a syscall is valid (i.e. that the caller is 54 + /// allowed to access it). This prevents programs from providing pointers into kernel memory or 55 + /// MMIO registers to certain syscalls to bypass memory protection. 56 + /// Panics if the pointer is not valid (TODO: proper error handling for syscalls). 57 + /// 58 + /// If the caller is not the kernel (determined by `pc`), check that the whole of the memory region 59 + /// starting at `addr` with length `len` bytes is entirely within either the program region of 60 + /// memory or flash. 61 + fn assert_address(addr: usize, len: usize, pc: usize) { 62 + // TODO: return proper errors from syscalls instead of panicking 63 + 64 + if !(FLASH_START..KERNEL_FLASH_END).contains(&pc) { 65 + assert!((PROGRAM_SRAM_START..SRAM_END).contains(&addr) || (FLASH_START..FLASH_END).contains(&addr)); 66 + assert!((PROGRAM_SRAM_START..=SRAM_END).contains(&(addr + len)) || (FLASH_START..=FLASH_END).contains(&(addr + len))); 44 67 } 45 68 }
+3 -3
eepy/src/syscall/exec.rs
··· 29 29 panic!("tried to exec invalid program"); 30 30 } 31 31 32 - stack_values.pc = core::mem::transmute((*program).entry); 33 - stack_values.lr = program_return_handler as *const u8; 32 + stack_values.pc = (*program).entry as *const () as usize; 33 + stack_values.lr = program_return_handler as *const () as usize; 34 34 35 35 // Move the saved registers to the top of program memory 36 36 // This makes sure all programs start with an empty program stack 37 37 if using_psp { 38 - let ptr: *mut StackFrame = SRAM_END.sub(size_of::<StackFrame>()).cast(); 38 + let ptr: *mut StackFrame = (SRAM_END - size_of::<StackFrame>()) as *mut StackFrame; 39 39 ptr.write(*stack_values); 40 40 asm!( 41 41 "msr psp, {ptr}",
+3 -1
eepy/src/syscall/image.rs
··· 4 4 use tp370pgh01::IMAGE_BYTES; 5 5 use crate::IMAGE_BUFFER; 6 6 use crate::core1::{ToCore0Message, ToCore1Message}; 7 - use super::StackFrame; 7 + use super::{assert_address, StackFrame}; 8 8 9 9 pub(super) fn handle_image(stack_values: &mut StackFrame) { 10 10 match ImageSyscall::from_repr(stack_values.r0) { ··· 16 16 17 17 fn handle_refresh(stack_values: &mut StackFrame) { 18 18 let fast_refresh = stack_values.r1 != 0; 19 + assert_address(stack_values.r2, IMAGE_BYTES, stack_values.pc); 19 20 let image: &[u8; IMAGE_BYTES] = unsafe { &*(stack_values.r2 as *const [u8; IMAGE_BYTES]) }; 20 21 critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image)); 21 22 ··· 30 31 31 32 fn handle_maybe_refresh(stack_values: &mut StackFrame) { 32 33 let fast_refresh = stack_values.r1 != 0; 34 + assert_address(stack_values.r2, IMAGE_BYTES, stack_values.pc); 33 35 let image: &[u8; IMAGE_BYTES] = unsafe { &*(stack_values.r2 as *const [u8; IMAGE_BYTES]) }; 34 36 critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image)); 35 37
+2 -1
eepy/src/syscall/input.rs
··· 3 3 use eepy_sys::input_common::Event; 4 4 use eepy_sys::SafeOption; 5 5 use crate::{EVENT_QUEUE, TOUCH_ENABLED}; 6 - use super::StackFrame; 6 + use super::{assert_address, StackFrame}; 7 7 8 8 pub(super) fn handle_input(stack_values: &mut StackFrame) { 9 9 match InputSyscall::from_repr(stack_values.r0) { ··· 16 16 17 17 fn handle_next_event(stack_values: &mut StackFrame) { 18 18 let next_event = critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).pop()); 19 + assert_address(stack_values.r1, size_of::<SafeOption<Event>>(), stack_values.pc); 19 20 let ptr = stack_values.r1 as *mut SafeOption<Event>; 20 21 unsafe { ptr.write(next_event.into()) }; 21 22 }
+24 -6
eepy/src/syscall/kv_store.rs
··· 5 5 use eepy_sys::kv_store::{AppendKeyError, DeleteKeyError, KvStoreSyscall, PutKeyError, ReadKeyArgs, ReadKeyError, WriteKeyArgs}; 6 6 use eepy_sys::SafeResult; 7 7 use crate::exception::StackFrame; 8 + use crate::syscall::assert_address; 8 9 use crate::tickv::{with_tickv, EepyFlashController}; 9 10 10 11 pub(super) fn handle_kv_store(stack_values: &mut StackFrame) { ··· 41 42 } 42 43 43 44 fn handle_kv_get(stack_values: &mut StackFrame) { 45 + assert_address(stack_values.r1, size_of::<ReadKeyArgs>(), stack_values.pc); 44 46 let args = stack_values.r1 as *const ReadKeyArgs; 45 - let key = unsafe { core::slice::from_raw_parts((*args).key_ptr, (*args).key_len) }; 46 - let buf = unsafe { core::slice::from_raw_parts_mut((*args).buf_ptr, (*args).buf_len) }; 47 + unsafe { 48 + assert_address((*args).key_ptr, (*args).key_len, stack_values.pc); 49 + assert_address((*args).buf_ptr, (*args).buf_len, stack_values.pc); 50 + } 51 + let key = unsafe { core::slice::from_raw_parts((*args).key_ptr as *const u8, (*args).key_len) }; 52 + let buf = unsafe { core::slice::from_raw_parts_mut((*args).buf_ptr as *mut u8, (*args).buf_len) }; 47 53 48 54 let hashed_key = hash(stack_values, key); 49 55 let res = unsafe { with_tickv(|tickv| tickv.get_key(hashed_key, buf)) }; ··· 57 63 } 58 64 59 65 fn handle_kv_append(stack_values: &mut StackFrame) { 66 + assert_address(stack_values.r1, size_of::<WriteKeyArgs>(), stack_values.pc); 60 67 let args = stack_values.r1 as *const WriteKeyArgs; 61 - let key = unsafe { core::slice::from_raw_parts((*args).key_ptr, (*args).key_len) }; 62 - let value = unsafe { core::slice::from_raw_parts((*args).value_ptr, (*args).value_len) }; 68 + unsafe { 69 + assert_address((*args).key_ptr, (*args).key_len, stack_values.pc); 70 + assert_address((*args).value_ptr, (*args).value_len, stack_values.pc); 71 + } 72 + let key = unsafe { core::slice::from_raw_parts((*args).key_ptr as *const u8, (*args).key_len) }; 73 + let value = unsafe { core::slice::from_raw_parts((*args).value_ptr as *const u8, (*args).value_len) }; 63 74 64 75 let hashed_key = hash(stack_values, key); 65 76 let res = unsafe { with_tickv(|tickv| append_key_gc(tickv, hashed_key, value)) }; ··· 73 84 } 74 85 75 86 fn handle_kv_put(stack_values: &mut StackFrame) { 87 + assert_address(stack_values.r1, size_of::<WriteKeyArgs>(), stack_values.pc); 76 88 let args = stack_values.r1 as *const WriteKeyArgs; 77 - let key = unsafe { core::slice::from_raw_parts((*args).key_ptr, (*args).key_len) }; 78 - let value = unsafe { core::slice::from_raw_parts((*args).value_ptr, (*args).value_len) }; 89 + unsafe { 90 + assert_address((*args).key_ptr, (*args).key_len, stack_values.pc); 91 + assert_address((*args).value_ptr, (*args).value_len, stack_values.pc); 92 + } 93 + let key = unsafe { core::slice::from_raw_parts((*args).key_ptr as *const u8, (*args).key_len) }; 94 + let value = unsafe { core::slice::from_raw_parts((*args).value_ptr as *const u8, (*args).value_len) }; 79 95 80 96 let hashed_key = hash(stack_values, key); 81 97 let mut res = unsafe { with_tickv(|tickv| append_key_gc(tickv, hashed_key, value)) }; ··· 97 113 } 98 114 99 115 fn handle_kv_invalidate(stack_values: &mut StackFrame) { 116 + assert_address(stack_values.r1, stack_values.r2, stack_values.pc); 100 117 let key = unsafe { core::slice::from_raw_parts(stack_values.r1 as *const u8, stack_values.r2) }; 101 118 let hashed_key = hash(stack_values, key); 102 119 let res = unsafe { with_tickv(|tickv| tickv.invalidate_key(hashed_key)) }; ··· 110 127 } 111 128 112 129 fn handle_kv_zeroise(stack_values: &mut StackFrame) { 130 + assert_address(stack_values.r1, stack_values.r2, stack_values.pc); 113 131 let key = unsafe { core::slice::from_raw_parts(stack_values.r1 as *const u8, stack_values.r2) }; 114 132 let hashed_key = hash(stack_values, key); 115 133 let res = unsafe { with_tickv(|tickv| tickv.zeroise_key(hashed_key)) };
+7 -4
eepy/src/syscall/misc.rs
··· 1 1 use defmt::{debug, error, info, trace, warn}; 2 - use eepy_sys::header::{SLOT_SIZE, XIP_BASE}; 2 + use eepy_sys::header::slot_of_addr; 3 3 use eepy_sys::misc::{LogLevel, MiscSyscall}; 4 4 use fw16_epd_bsp::pac::Peripherals; 5 5 use crate::{SERIAL_NUMBER}; 6 - use super::StackFrame; 6 + use super::{assert_address, StackFrame}; 7 7 8 8 pub(super) fn handle_misc(stack_values: &mut StackFrame) { 9 9 match MiscSyscall::from_repr(stack_values.r0) { ··· 15 15 } 16 16 17 17 fn handle_get_serial(stack_values: &mut StackFrame) { 18 + assert_address(stack_values.r1, 16, stack_values.pc); 18 19 let buf = stack_values.r1 as *mut [u8; 16]; 19 20 unsafe { 20 21 (*buf).copy_from_slice(SERIAL_NUMBER.get().unwrap()); ··· 24 25 fn handle_log_message(stack_values: &mut StackFrame) { 25 26 let log_level = LogLevel::from_repr(stack_values.r1).expect("invalid log level"); 26 27 let len = stack_values.r2; 28 + assert_address(stack_values.r3, len, stack_values.pc); 27 29 let ptr = stack_values.r3 as *const u8; 28 - let s = unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr, len)) }; 29 - let slot_n = (stack_values.pc as usize - XIP_BASE as usize) / SLOT_SIZE; 30 + let msg = unsafe { core::str::from_utf8(core::slice::from_raw_parts(ptr, len)) }; 31 + let s = msg.unwrap_or("<invalid UTF-8 string>"); 32 + let slot_n = slot_of_addr(stack_values.pc); 30 33 31 34 match log_level { 32 35 LogLevel::Trace => trace!("[PROGRAM:{}] {}", slot_n, s),
+6 -7
eepy/src/usb.rs
··· 4 4 use core::sync::atomic::Ordering; 5 5 use critical_section::Mutex; 6 6 use defmt::{trace, warn}; 7 - use portable_atomic::AtomicPtr; 7 + use portable_atomic::AtomicUsize; 8 8 use eepy_sys::usb::{SafePollResult, SafeUsbError, UsbEpAllocArgs, UsbReadArgs, UsbSyscall, UsbWriteArgs}; 9 9 use fw16_epd_bsp::hal::clocks::UsbClock; 10 10 use fw16_epd_bsp::hal::usb::UsbBus; ··· 94 94 )); 95 95 } 96 96 97 - static USB_HANDLER: AtomicPtr<u8> = AtomicPtr::new(core::ptr::null_mut()); 97 + static USB_HANDLER: AtomicUsize = AtomicUsize::new(0); 98 98 99 99 unsafe extern "C" { 100 100 fn usb_ret(); ··· 109 109 } 110 110 111 111 let handler = USB_HANDLER.load(Ordering::Relaxed); 112 - if handler.is_null() { 112 + if handler == 0 { 113 113 warn!("USB handler is null but interrupt was not masked"); 114 114 return; 115 115 } ··· 128 128 r2: 0, 129 129 r3: 0, 130 130 r12: 0, 131 - lr: usb_ret as *const u8, 131 + lr: usb_ret as *const () as usize, 132 132 pc: handler, 133 133 xpsr: 0x1000000, 134 134 }); ··· 161 161 } 162 162 163 163 pub(crate) fn handle_clear_handler() { 164 - USB_HANDLER.store(core::ptr::null_mut(), Ordering::Relaxed); 164 + USB_HANDLER.store(0, Ordering::Relaxed); 165 165 trace!("masking USBCTRL_IRQ"); 166 166 pac::NVIC::mask(interrupt::USBCTRL_IRQ); 167 167 } 168 168 169 169 fn handle_set_handler(stack_values: &mut StackFrame) { 170 - let handler = stack_values.r1 as *mut u8; 171 - USB_HANDLER.store(handler, Ordering::Relaxed); 170 + USB_HANDLER.store(stack_values.r1, Ordering::Relaxed); 172 171 unsafe { 173 172 trace!("unmasking USBCTRL_IRQ"); 174 173 pac::NVIC::unmask(interrupt::USBCTRL_IRQ);