mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-15 16:26:48 +00:00
Reimplement reading cstring from user space
This commit is contained in:
parent
6ab6648b33
commit
10690063eb
@ -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<T: Pod>(src: Vaddr) -> Result<T> {
|
||||
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<T: Pod>(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:
|
||||
/// <https://elixir.bootlin.com/linux/v6.0.9/source/lib/strncpy_from_user.c#L28>
|
||||
pub fn read_cstring_from_user(addr: Vaddr, max_len: usize) -> Result<CString> {
|
||||
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<u8> = 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::<u8>(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::<u8>();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Handle the first few bytes to make `cur_addr` aligned with `size_of::<usize>`
|
||||
read_one_byte_at_a_time_while!(
|
||||
cur_addr % mem::size_of::<usize>() != 0 && buffer.len() < max_len
|
||||
);
|
||||
|
||||
// Handle the rest of the bytes in bulk
|
||||
while (buffer.len() + mem::size_of::<usize>()) <= max_len {
|
||||
let Ok(word) = read_val_from_user::<usize>(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::<usize>();
|
||||
}
|
||||
|
||||
// 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:
|
||||
/// <https://elixir.bootlin.com/linux/v6.0.9/source/include/asm-generic/word-at-a-time.h#L93>
|
||||
const fn has_zero(value: usize) -> bool {
|
||||
const ONE_BITS: usize = usize::from_le_bytes([0x01; mem::size_of::<usize>()]);
|
||||
const HIGH_BITS: usize = usize::from_le_bytes([0x80; mem::size_of::<usize>()]);
|
||||
|
||||
value.wrapping_sub(ONE_BITS) & !value & HIGH_BITS != 0
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user