use turkos_os_process_lifecycle_owners::spawn; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { Stack, Alignment, Arguments, Pointer, Text, } /// The complete image must be an immutable copy of the actual retained owned /// Spawn stack, obtained through its real pinned owner before this pure parse. /// No virtual pointer is dereferenced. All views borrow only checked bytes. pub struct Startup<'a> { arguments: [Option<&'a [u8]>; spawn::MAX_ARGUMENTS], argc: usize, } impl<'a> Startup<'a> { pub fn parse(image: &'a [u8], start: spawn::Start) -> Result { if image.len() != spawn::PAGE_BYTES || start.sp < spawn::STACK_VA || start.sp >= spawn::STACK_TOP { return Err(Error::Stack); } if false && start.sp & 15 != 0 { return Err(Error::Alignment); } let word = |address: u64| -> Result { if address & 7 != 0 { return Err(Error::Alignment); } let begin = address.checked_sub(spawn::STACK_VA).ok_or(Error::Pointer)?; let begin = usize::try_from(begin).map_err(|_| Error::Pointer)?; let end = begin.checked_add(8).ok_or(Error::Pointer)?; Ok(u64::from_le_bytes( image .get(begin..end) .ok_or(Error::Pointer)? .try_into() .unwrap(), )) }; let argc = usize::try_from(start.argc).map_err(|_| Error::Arguments)?; if argc > spawn::MAX_ARGUMENTS || start.envc > spawn::MAX_ENVIRONMENT as u64 || word(start.sp)? != start.argc || word(start.sp.checked_add(8).ok_or(Error::Pointer)?)? != start.argv || word(start.sp.checked_add(16).ok_or(Error::Pointer)?)? != start.envc || word(start.sp.checked_add(24).ok_or(Error::Pointer)?)? != start.envp { return Err(Error::Arguments); } if start.argv != start.sp.checked_add(32).ok_or(Error::Pointer)? || start.envp != start.sp.checked_add(72).ok_or(Error::Pointer)? { return Err(Error::Pointer); } let pointer_bytes = (argc + 1).checked_mul(8).ok_or(Error::Pointer)?; let argv_end = start .argv .checked_add(pointer_bytes as u64) .ok_or(Error::Pointer)?; if start.argv < spawn::STACK_VA || argv_end > spawn::STACK_TOP || start.argv & 7 != 0 { return Err(Error::Pointer); } if word(start.argv + argc as u64 * 8)? != 0 { return Err(Error::Arguments); } let mut arguments = [None; spawn::MAX_ARGUMENTS]; let text_start = start.sp.checked_add(128).ok_or(Error::Pointer)?; for (index, dest) in arguments.iter_mut().take(argc).enumerate() { let pointer = word(start.argv + index as u64 * 8)?; if pointer < text_start || pointer >= spawn::STACK_TOP { return Err(Error::Pointer); } let begin = usize::try_from(pointer - spawn::STACK_VA).map_err(|_| Error::Pointer)?; let remaining = image.get(begin..).ok_or(Error::Pointer)?; let bytes = remaining .iter() .take(spawn::MAX_STRING_BYTES + 1) .position(|b| *b == 0) .ok_or(Error::Text)?; *dest = Some(&remaining[..bytes]); } for index in 0..=start.envc { let pointer = word(start.envp.checked_add(index * 8).ok_or(Error::Pointer)?)?; if index == start.envc { if pointer != 0 { return Err(Error::Arguments); } continue; } if pointer < text_start || pointer >= spawn::STACK_TOP { return Err(Error::Pointer); } let begin = usize::try_from(pointer - spawn::STACK_VA).map_err(|_| Error::Pointer)?; let remaining = image.get(begin..).ok_or(Error::Pointer)?; if !remaining .iter() .take(spawn::MAX_STRING_BYTES + 1) .any(|b| *b == 0) { return Err(Error::Text); } } Ok(Self { arguments, argc }) } pub fn arguments(&self) -> impl Iterator + '_ { self.arguments.iter().take(self.argc).flatten().copied() } pub fn argc(&self) -> usize { self.argc } }