feature: extended user pointer validation
This commit is contained in:
parent
1820009dee
commit
7c809f3b11
@ -88,8 +88,9 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
|
||||
match err_code {
|
||||
EC_DATA_ABORT_EL0 | EC_DATA_ABORT_ELX => {
|
||||
let far = FAR_EL1.get() as usize;
|
||||
let iss = esr & 0x1FFFFFF;
|
||||
|
||||
if far < mem::KERNEL_OFFSET && sched::is_ready() {
|
||||
if iss & (1 << 6) != 0 && far < mem::KERNEL_OFFSET && sched::is_ready() {
|
||||
let thread = Thread::current();
|
||||
let proc = thread.owner().unwrap();
|
||||
|
||||
|
@ -8,7 +8,7 @@ use libsys::{
|
||||
ioctl::IoctlCmd
|
||||
};
|
||||
use core::mem::size_of;
|
||||
use crate::syscall::arg::validate_user_ptr_struct;
|
||||
use crate::syscall::arg;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CharRingInner<const N: usize> {
|
||||
@ -45,12 +45,12 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
match cmd {
|
||||
IoctlCmd::TtyGetAttributes => {
|
||||
// TODO validate size
|
||||
let res = validate_user_ptr_struct::<Termios>(ptr)?;
|
||||
let res = arg::struct_mut::<Termios>(ptr)?;
|
||||
*res = self.ring().config.lock().clone();
|
||||
Ok(size_of::<Termios>())
|
||||
},
|
||||
IoctlCmd::TtySetAttributes => {
|
||||
let src = validate_user_ptr_struct::<Termios>(ptr)?;
|
||||
let src = arg::struct_ref::<Termios>(ptr)?;
|
||||
*self.ring().config.lock() = src.clone();
|
||||
Ok(size_of::<Termios>())
|
||||
},
|
||||
|
@ -271,12 +271,17 @@ impl Space {
|
||||
todo!();
|
||||
// res.map(virt_addr, dst_phys, flags)?;
|
||||
} else {
|
||||
// TODO only apply CoW to writable pages
|
||||
flags |= MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW;
|
||||
l2_table[l2i].set_cow();
|
||||
unsafe {
|
||||
asm!("tlbi vaae1, {}", in(reg) virt_addr);
|
||||
let writable = flags & MapAttributes::AP_BOTH_READONLY == MapAttributes::AP_BOTH_READWRITE;
|
||||
|
||||
if writable {
|
||||
flags |= MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW;
|
||||
l2_table[l2i].set_cow();
|
||||
|
||||
unsafe {
|
||||
asm!("tlbi vaae1, {}", in(reg) virt_addr);
|
||||
}
|
||||
}
|
||||
|
||||
res.map(virt_addr, dst_phys, flags)?;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! System call argument ABI helpers
|
||||
|
||||
use crate::mem;
|
||||
use core::alloc::Layout;
|
||||
use core::mem::size_of;
|
||||
use libsys::error::Errno;
|
||||
|
||||
@ -22,34 +23,62 @@ macro_rules! invalid_memory {
|
||||
}
|
||||
}
|
||||
|
||||
fn translate(virt: usize) -> Option<usize> {
|
||||
#[inline(always)]
|
||||
fn is_el0_accessible(virt: usize, write: bool) -> bool {
|
||||
let mut res: usize;
|
||||
unsafe {
|
||||
asm!("at s1e1r, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
|
||||
}
|
||||
if res & 1 == 0 {
|
||||
Some(res & !(0xFFF | (0xFF << 56)))
|
||||
} else {
|
||||
None
|
||||
if write {
|
||||
asm!("at s1e0w, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
|
||||
} else {
|
||||
asm!("at s1e0r, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
|
||||
}
|
||||
}
|
||||
res & 1 == 0
|
||||
}
|
||||
|
||||
/// Unwraps a slim structure pointer
|
||||
pub fn validate_user_ptr_struct<'a, T>(base: usize) -> Result<&'a mut T, Errno> {
|
||||
validate_user_ptr_struct_option(base).and_then(|e| e.ok_or(Errno::InvalidArgument))
|
||||
pub fn struct_ref<'a, T>(base: usize) -> Result<&'a T, Errno> {
|
||||
let layout = Layout::new::<T>();
|
||||
if base % layout.align() != 0 {
|
||||
invalid_memory!(
|
||||
"Structure pointer is misaligned: base={:#x}, expected {:?}",
|
||||
base,
|
||||
layout
|
||||
);
|
||||
}
|
||||
let bytes = buf_ref(base, layout.size())?;
|
||||
Ok(unsafe { &*(bytes.as_ptr() as *const T) })
|
||||
}
|
||||
|
||||
pub fn validate_user_ptr_struct_option<'a, T>(base: usize) -> Result<Option<&'a mut T>, Errno> {
|
||||
pub fn struct_mut<'a, T>(base: usize) -> Result<&'a mut T, Errno> {
|
||||
let layout = Layout::new::<T>();
|
||||
if base % layout.align() != 0 {
|
||||
invalid_memory!(
|
||||
"Structure pointer is misaligned: base={:#x}, expected {:?}",
|
||||
base,
|
||||
layout
|
||||
);
|
||||
}
|
||||
let bytes = buf_mut(base, layout.size())?;
|
||||
Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) })
|
||||
}
|
||||
|
||||
pub fn option_struct_ref<'a, T>(base: usize) -> Result<Option<&'a T>, Errno> {
|
||||
if base == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
let bytes = validate_user_ptr(base, size_of::<T>())?;
|
||||
Ok(Some(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) }))
|
||||
struct_ref(base).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwraps an user buffer reference
|
||||
pub fn validate_user_ptr<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Errno> {
|
||||
pub fn option_struct_mut<'a, T>(base: usize) -> Result<Option<&'a mut T>, Errno> {
|
||||
if base == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
struct_mut(base).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_ptr(base: usize, len: usize, writable: bool) -> Result<(), Errno> {
|
||||
if base > mem::KERNEL_OFFSET || base + len > mem::KERNEL_OFFSET {
|
||||
invalid_memory!(
|
||||
"User region refers to kernel memory: base={:#x}, len={:#x}",
|
||||
@ -59,9 +88,9 @@ pub fn validate_user_ptr<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Er
|
||||
}
|
||||
|
||||
for i in (base / mem::PAGE_SIZE)..((base + len + mem::PAGE_SIZE - 1) / mem::PAGE_SIZE) {
|
||||
if translate(i * mem::PAGE_SIZE).is_none() {
|
||||
if !is_el0_accessible(i * mem::PAGE_SIZE, writable) {
|
||||
invalid_memory!(
|
||||
"User region refers to unmapped memory: base={:#x}, len={:#x} (page {:#x})",
|
||||
"User region refers to inaccessible/unmapped memory: base={:#x}, len={:#x} (page {:#x})",
|
||||
base,
|
||||
len,
|
||||
i * mem::PAGE_SIZE
|
||||
@ -69,21 +98,38 @@ pub fn validate_user_ptr<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Er
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn buf_ref<'a>(base: usize, len: usize) -> Result<&'a [u8], Errno> {
|
||||
validate_ptr(base, len, false)?;
|
||||
Ok(unsafe { core::slice::from_raw_parts(base as *const u8, len) })
|
||||
}
|
||||
|
||||
pub fn buf_mut<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Errno> {
|
||||
validate_ptr(base, len, true)?;
|
||||
Ok(unsafe { core::slice::from_raw_parts_mut(base as *mut u8, len) })
|
||||
}
|
||||
|
||||
/// Unwraps a nullable user buffer reference
|
||||
pub fn validate_user_ptr_null<'a>(base: usize, len: usize) -> Result<Option<&'a mut [u8]>, Errno> {
|
||||
pub fn option_buf_ref<'a>(base: usize, len: usize) -> Result<Option<&'a [u8]>, Errno> {
|
||||
if base == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
validate_user_ptr(base, len).map(Some)
|
||||
buf_ref(base, len).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn option_buf_mut<'a>(base: usize, len: usize) -> Result<Option<&'a mut [u8]>, Errno> {
|
||||
if base == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
buf_mut(base, len).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwraps user string argument
|
||||
pub fn validate_user_str<'a>(base: usize, len: usize) -> Result<&'a str, Errno> {
|
||||
let bytes = validate_user_ptr(base, len)?;
|
||||
pub fn str_ref<'a>(base: usize, len: usize) -> Result<&'a str, Errno> {
|
||||
let bytes = buf_ref(base, len)?;
|
||||
core::str::from_utf8(bytes).map_err(|_| {
|
||||
warnln!(
|
||||
"User string contains invalid UTF-8 characters: base={:#x}, len={:#x}",
|
||||
|
@ -19,7 +19,6 @@ use libsys::{
|
||||
use vfs::VnodeRef;
|
||||
|
||||
pub mod arg;
|
||||
pub use arg::*;
|
||||
|
||||
/// Creates a "fork" process from current one using its register frame.
|
||||
/// See [Process::fork()].
|
||||
@ -59,7 +58,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
let proc = Process::current();
|
||||
let fd = FileDescriptor::from(args[0] as u32);
|
||||
let mut io = proc.io.lock();
|
||||
let buf = validate_user_ptr(args[1], args[2])?;
|
||||
let buf = arg::buf_mut(args[1], args[2])?;
|
||||
|
||||
io.file(fd)?.borrow_mut().read(buf)
|
||||
},
|
||||
@ -67,13 +66,13 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
let proc = Process::current();
|
||||
let fd = FileDescriptor::from(args[0] as u32);
|
||||
let mut io = proc.io.lock();
|
||||
let buf = validate_user_ptr(args[1], args[2])?;
|
||||
let buf = arg::buf_ref(args[1], args[2])?;
|
||||
|
||||
io.file(fd)?.borrow_mut().write(buf)
|
||||
},
|
||||
SystemCall::Open => {
|
||||
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
|
||||
let path = validate_user_str(args[1], args[2])?;
|
||||
let path = arg::str_ref(args[1], args[2])?;
|
||||
let mode = FileMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
|
||||
let opts = OpenFlags::from_bits(args[4] as u32).ok_or(Errno::InvalidArgument)?;
|
||||
|
||||
@ -99,8 +98,8 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
},
|
||||
SystemCall::FileStatus => {
|
||||
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
|
||||
let filename = validate_user_str(args[1], args[2])?;
|
||||
let buf = validate_user_ptr_struct::<Stat>(args[3])?;
|
||||
let filename = arg::str_ref(args[1], args[2])?;
|
||||
let buf = arg::struct_mut::<Stat>(args[3])?;
|
||||
let flags = args[4] as u32;
|
||||
|
||||
let proc = Process::current();
|
||||
@ -119,8 +118,8 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
node.ioctl(cmd, args[2], args[3])
|
||||
},
|
||||
SystemCall::Select => {
|
||||
let rfds = validate_user_ptr_struct_option::<FdSet>(args[0])?;
|
||||
let wfds = validate_user_ptr_struct_option::<FdSet>(args[1])?;
|
||||
let rfds = arg::option_struct_mut::<FdSet>(args[0])?;
|
||||
let wfds = arg::option_struct_mut::<FdSet>(args[1])?;
|
||||
let timeout = if args[2] == 0 {
|
||||
None
|
||||
} else {
|
||||
@ -131,7 +130,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
},
|
||||
SystemCall::Access => {
|
||||
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
|
||||
let path = validate_user_str(args[1], args[2])?;
|
||||
let path = arg::str_ref(args[1], args[2])?;
|
||||
let mode = AccessMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
|
||||
let flags = args[4] as u32;
|
||||
|
||||
@ -156,14 +155,14 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
let node = {
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
let filename = validate_user_str(args[0], args[1])?;
|
||||
let filename = arg::str_ref(args[0], args[1])?;
|
||||
// TODO argv, envp array passing ABI?
|
||||
let node = io.ioctx().find(None, filename, true)?;
|
||||
drop(io);
|
||||
node
|
||||
};
|
||||
let file = node.open(OpenFlags::O_RDONLY)?;
|
||||
Process::execve(|space| elf::load_elf(space, file), 0).unwrap();
|
||||
Process::execve(move |space| elf::load_elf(space, file), 0).unwrap();
|
||||
panic!();
|
||||
},
|
||||
SystemCall::Exit => {
|
||||
@ -181,7 +180,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
SystemCall::WaitPid => {
|
||||
// TODO special "pid" values
|
||||
let pid = unsafe { Pid::from_raw(args[0] as u32) };
|
||||
let status = validate_user_ptr_struct::<i32>(args[1])?;
|
||||
let status = arg::struct_mut::<i32>(args[1])?;
|
||||
|
||||
match Process::waitpid(pid) {
|
||||
Ok(exit) => {
|
||||
@ -204,7 +203,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
SystemCall::GetPid => todo!(),
|
||||
SystemCall::GetTid => Ok(Thread::current().id() as usize),
|
||||
SystemCall::Sleep => {
|
||||
let rem_buf = validate_user_ptr_null(args[1], size_of::<u64>() * 2)?;
|
||||
let rem_buf = arg::option_buf_ref(args[1], size_of::<u64>() * 2)?;
|
||||
let mut rem = Duration::new(0, 0);
|
||||
let res = wait::sleep(Duration::from_nanos(args[0] as u64), &mut rem);
|
||||
if res == Err(Errno::Interrupt) {
|
||||
@ -249,11 +248,9 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
|
||||
// Debugging
|
||||
SystemCall::DebugTrace => {
|
||||
let buf = validate_user_ptr(args[0], args[1])?;
|
||||
let buf = arg::str_ref(args[0], args[1])?;
|
||||
print!(Level::Debug, "[trace] ");
|
||||
for &byte in buf.iter() {
|
||||
print!(Level::Debug, "{}", byte as char);
|
||||
}
|
||||
print!(Level::Debug, "{}", buf);
|
||||
println!(Level::Debug, "");
|
||||
Ok(args[1])
|
||||
},
|
||||
|
@ -41,16 +41,6 @@ fn main() -> i32 {
|
||||
let mut buf = [0; 256];
|
||||
let mut stdin = io::stdin();
|
||||
|
||||
let delay = libusr::thread::spawn(|| {
|
||||
let mut t = [0; 2];
|
||||
libusr::sys::sys_ex_nanosleep(1_000_000_000, &mut t);
|
||||
});
|
||||
|
||||
delay.join();
|
||||
|
||||
libusr::signal::set_handler(libusr::sys::Signal::Interrupt, libusr::signal::SignalHandler::Ignore);
|
||||
libusr::sys::sys_ex_kill(libusr::sys::SignalDestination::This, libusr::sys::Signal::Interrupt);
|
||||
|
||||
loop {
|
||||
print!("> ");
|
||||
let line = readline(&mut stdin, &mut buf).unwrap();
|
||||
|
Loading…
x
Reference in New Issue
Block a user