osmium icon indicating copy to clipboard operation
osmium copied to clipboard

Unsoundness in receive_data

Open lwz23 opened this issue 9 months ago • 1 comments

Hello, thank you for your contribution in this project, I an testing our static analysis tool in github's Rust project and I notice the following code:

fn execve(
    filename: u32,
    filename_length: u32,
    argv: u32,
    envp: u32,
    tf: &mut trap::TrapFrame,
    k: &mut kernel::Kernel,
) -> Result<u32, SyscallError> {
    let name: &str;
    unsafe {
        match str::from_utf8(
            slice::from_raw_parts(filename as *const u8, filenamelength as usize),
        ) {
            Ok(f) => name = f,
            Err() => {
                return Err(SyscallError::InvalidArguments);
            }
        };
    }
    let file = match files::search(name) {
        Some(file) => file,
        None => return Err(SyscallError::NotFound),
    };
    let e = match elf::Elf::new(file.bytes) {
        Ok(e) => e,
        Err(_) => return Err(SyscallError::IllegalFile),
    };
    match k.current_process.as_mut().unwrap().load_elf(&e, &mut k.allocator) {
        Ok(()) => {}
        Err(e) => return Err(SyscallError::IllegalFile),
    };
    dprintln!("set entry point: {:x}", e.elf.entry);
    let new_tf = trap::TrapFrame::new(e.elf.entry, memlayout::USER_STACK_BOTTOMN);
    *tf = new_tf;
    Ok(0)
}

The unsoundness occurs in the execve function where:

It takes the filename parameter (which is just a u32) and casts it directly to a raw pointer It uses slice::from_raw_parts(filename as *const u8, filename_length as usize) without any validation If filename points to invalid memory or if filename_length is too large, it will attempt to read invalid memory before even checking if the data is valid UTF-8

While the function does check if the bytes are valid UTF-8 after creating the slice, this happens too late - the memory safety violation occurs during the from_raw_parts call itself when the memory is accessed. There are no guards, bounds checks, or validations before the unsafe operation, creating a direct path from user input to memory safety violation. A valid path to call this fn: pub fn syscall_dispatch -> fn execve

POC

fn main() {
    // Create a syscall with an invalid memory address
    let invalid_syscall = Syscall::Execve {
        filename: 0xDEADBEEF,  // Invalid memory address
        filename_length: 10,    // Attempt to read 10 bytes from invalid address
        argv: 0,
        envp: 0
    };
    
    // Create kernel and trap frame
    let mut kernel = Kernel::new();
    let mut trap_frame = TrapFrame::default();
    
    // This will trigger undefined behavior
    let result = syscall_dispatch(invalid_syscall, &mut kernel, &mut trap_frame);
}

lwz23 avatar Mar 04 '25 11:03 lwz23

another samilar case is: pub fn syscall_dispatch -> fn receive_data

lwz23 avatar Mar 04 '25 11:03 lwz23