From 10690063eb9d6ebf09fde1a7a3b767803f73fdda Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Tue, 9 Jan 2024 03:28:01 +0000 Subject: [PATCH] Reimplement reading cstring from user space --- kernel/aster-nix/src/util/mod.rs | 88 ++++++++++++++++++++++++++--- kernel/aster-nix/src/vm/vmar/mod.rs | 2 +- 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/kernel/aster-nix/src/util/mod.rs b/kernel/aster-nix/src/util/mod.rs index 41fdb87c4..3d63dc352 100644 --- a/kernel/aster-nix/src/util/mod.rs +++ b/kernel/aster-nix/src/util/mod.rs @@ -1,41 +1,113 @@ // SPDX-License-Identifier: MPL-2.0 +use core::mem; + use aster_frame::vm::VmIo; use crate::prelude::*; pub mod net; -/// copy bytes from user space of current process. The bytes len is the len of dest. +/// Read bytes into the `dest` buffer +/// from the user space of the current process. +/// If successful, +/// the `dest` buffer is filled with exact `dest.len` bytes. pub fn read_bytes_from_user(src: Vaddr, dest: &mut [u8]) -> Result<()> { let current = current!(); let root_vmar = current.root_vmar(); Ok(root_vmar.read_bytes(src, dest)?) } -/// copy val (Plain of Data type) from user space of current process. +/// Read a value of `Pod` type +/// from the user space of the current process. pub fn read_val_from_user(src: Vaddr) -> Result { let current = current!(); let root_vmar = current.root_vmar(); Ok(root_vmar.read_val(src)?) } -/// write bytes from user space of current process. The bytes len is the len of src. +/// Write bytes from the `src` buffer +/// to the user space of the current process. If successful, +/// the write length will be equal to `src.len`. pub fn write_bytes_to_user(dest: Vaddr, src: &[u8]) -> Result<()> { let current = current!(); let root_vmar = current.root_vmar(); Ok(root_vmar.write_bytes(dest, src)?) } -/// write val (Plain of Data type) to user space of current process. +/// Write `val` to the user space of the current process. pub fn write_val_to_user(dest: Vaddr, val: &T) -> Result<()> { let current = current!(); let root_vmar = current.root_vmar(); Ok(root_vmar.write_val(dest, val)?) } -/// read a cstring from user, the length of cstring should not exceed max_len(include null byte) +/// Read a C string from the user space of the current process. +/// The length of the string should not exceed `max_len`, +/// including the final `\0` byte. +/// +/// This implementation is inspired by +/// the `do_strncpy_from_user` function in Linux kernel. +/// The original Linux implementation can be found at: +/// pub fn read_cstring_from_user(addr: Vaddr, max_len: usize) -> Result { - let mut buffer = vec![0u8; max_len]; - read_bytes_from_user(addr, &mut buffer)?; - Ok(CString::from(CStr::from_bytes_until_nul(&buffer)?)) + let mut buffer: Vec = Vec::with_capacity(max_len); + let mut cur_addr = addr; + + macro_rules! read_one_byte_at_a_time_while { + ($cond:expr) => { + while $cond { + let byte = read_val_from_user::(cur_addr)?; + buffer.push(byte); + if byte == 0 { + return Ok(CString::from_vec_with_nul(buffer) + .expect("We provided 0 but no 0 is found")); + } + cur_addr += mem::size_of::(); + } + }; + } + + // Handle the first few bytes to make `cur_addr` aligned with `size_of::` + read_one_byte_at_a_time_while!( + cur_addr % mem::size_of::() != 0 && buffer.len() < max_len + ); + + // Handle the rest of the bytes in bulk + while (buffer.len() + mem::size_of::()) <= max_len { + let Ok(word) = read_val_from_user::(cur_addr) else { + break; + }; + + if has_zero(word) { + for byte in word.to_ne_bytes() { + buffer.push(byte); + if byte == 0 { + return Ok(CString::from_vec_with_nul(buffer) + .expect("We provided 0 but no 0 is found")); + } + } + unreachable!("The branch should never be reached unless `has_zero` has bugs.") + } + + buffer.extend_from_slice(&word.to_ne_bytes()); + + cur_addr += mem::size_of::(); + } + + // Handle the last few bytes that are not enough for a word + read_one_byte_at_a_time_while!(buffer.len() < max_len); + + // Maximum length exceeded before finding the null terminator + return_errno_with_message!(Errno::EFAULT, "Fails to read CString from user"); +} + +/// Determine whether the value contains a zero byte. +/// +/// This magic algorithm is from the Linux `has_zero` function: +/// +const fn has_zero(value: usize) -> bool { + const ONE_BITS: usize = usize::from_le_bytes([0x01; mem::size_of::()]); + const HIGH_BITS: usize = usize::from_le_bytes([0x80; mem::size_of::()]); + + value.wrapping_sub(ONE_BITS) & !value & HIGH_BITS != 0 } diff --git a/kernel/aster-nix/src/vm/vmar/mod.rs b/kernel/aster-nix/src/vm/vmar/mod.rs index e2e07874a..2cfd1d3e4 100644 --- a/kernel/aster-nix/src/vm/vmar/mod.rs +++ b/kernel/aster-nix/src/vm/vmar/mod.rs @@ -730,7 +730,7 @@ impl Vmar_ { return Ok(vm_mapping.clone()); } - return_errno_with_message!(Errno::EACCES, "No mapped vmo at this offset"); + return_errno_with_message!(Errno::EFAULT, "No mapped vmo at this offset"); } }