From fec8f4aa0c2f0eb1de14603c38646b80dde7d3bc Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 3 Feb 2021 17:37:09 +0100 Subject: [PATCH] Use physical addresses internally for futexes. This solves a bug, that allows processes in different address spaces to be the target of a futex wakeup call, even though that process is in another address space! --- src/syscall/futex.rs | 49 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/syscall/futex.rs b/src/syscall/futex.rs index ee50c433..9e04feb1 100644 --- a/src/syscall/futex.rs +++ b/src/syscall/futex.rs @@ -9,12 +9,19 @@ use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::context::{self, Context}; use crate::time; +use crate::memory::PhysicalAddress; +use crate::paging::{ActivePageTable, VirtualAddress}; use crate::syscall::data::TimeSpec; -use crate::syscall::error::{Error, Result, ESRCH, EAGAIN, EINVAL}; +use crate::syscall::error::{Error, Result, ESRCH, EAGAIN, EFAULT, EINVAL}; use crate::syscall::flag::{FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE}; use crate::syscall::validate::{validate_slice, validate_slice_mut}; -type FutexList = VecDeque<(usize, Arc<RwLock<Context>>)>; +type FutexList = VecDeque<FutexEntry>; + +pub struct FutexEntry { + target_physaddr: PhysicalAddress, + context_lock: Arc<RwLock<Context>>, +} /// Fast userspace mutex list static FUTEXES: Once<RwLock<FutexList>> = Once::new(); @@ -34,7 +41,17 @@ pub fn futexes_mut() -> RwLockWriteGuard<'static, FutexList> { FUTEXES.call_once(init_futexes).write() } +// FIXME: Don't take a mutable reference to the addr, since rustc can make assumptions that the +// pointee cannot be changed by another thread, which could make atomic ops useless. pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32) -> Result<usize> { + let target_physaddr = unsafe { + let mut active_table = ActivePageTable::new(); + let virtual_address = VirtualAddress::new(addr as *mut i32 as usize); + + // FIXME: Already validated in syscall/mod.rs + active_table.translate(virtual_address).ok_or(Error::new(EFAULT))? + }; + match op { FUTEX_WAIT => { let timeout_opt = if val2 != 0 { @@ -69,7 +86,10 @@ pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32) context.block("futex"); } - futexes.push_back((addr as *mut i32 as usize, context_lock)); + futexes.push_back(FutexEntry { + target_physaddr, + context_lock, + }); } unsafe { context::switch(); } @@ -97,9 +117,10 @@ pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32) let mut i = 0; while i < futexes.len() && (woken as i32) < val { - if futexes[i].0 == addr as *mut i32 as usize { + if futexes[i].target_physaddr == target_physaddr { if let Some(futex) = futexes.swap_remove_back(i) { - futex.1.write().unblock(); + let mut context_guard = futex.context_lock.write(); + context_guard.unblock(); woken += 1; } } else { @@ -111,7 +132,15 @@ pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32) Ok(woken) }, FUTEX_REQUEUE => { - let addr2_safe = validate_slice_mut(addr2, 1).map(|addr2_safe| &mut addr2_safe[0])?; + let addr2_physaddr = unsafe { + let mut active_table = ActivePageTable::new(); + + let addr2_safe = validate_slice_mut(addr2, 1).map(|addr2_safe| &mut addr2_safe[0])?; + let addr2_virt = VirtualAddress::new(addr2_safe as *mut i32 as usize); + + // FIXME: Already validated. + active_table.translate(addr2_virt).ok_or(Error::new(EFAULT))? + }; let mut woken = 0; let mut requeued = 0; @@ -121,9 +150,9 @@ pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32) let mut i = 0; while i < futexes.len() && (woken as i32) < val { - if futexes[i].0 == addr as *mut i32 as usize { + if futexes[i].target_physaddr == target_physaddr { if let Some(futex) = futexes.swap_remove_back(i) { - futex.1.write().unblock(); + futex.context_lock.write().unblock(); woken += 1; } } else { @@ -131,8 +160,8 @@ pub fn futex(addr: &mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32) } } while i < futexes.len() && requeued < val2 { - if futexes[i].0 == addr as *mut i32 as usize { - futexes[i].0 = addr2_safe as *mut i32 as usize; + if futexes[i].target_physaddr == target_physaddr { + futexes[i].target_physaddr = addr2_physaddr; requeued += 1; } i += 1; -- GitLab