Move proc code to userspace.

This commit is contained in:
4lDO2
2024-12-17 00:20:42 +01:00
parent 1b9fcbf593
commit 1d5f8fd46d
11 changed files with 44 additions and 1196 deletions
+12 -11
View File
@@ -18,7 +18,7 @@ use crate::{
memory::{allocate_p2frame, deallocate_p2frame, Enomem, Frame, RaiiFrame},
paging::{RmmA, RmmArch},
percpu::PercpuBlock,
scheme::FileHandle,
scheme::{FileHandle, SchemeNamespace},
};
use crate::syscall::error::{Error, Result, EAGAIN, ESRCH};
@@ -26,11 +26,9 @@ use crate::syscall::error::{Error, Result, EAGAIN, ESRCH};
use super::{
empty_cr3,
memory::{AddrSpaceWrapper, GrantFileRef},
process::{Process, ProcessId},
};
/// The status of a context - used for scheduling
/// See `syscall::process::waitpid` and the `sync` module for examples of usage
#[derive(Clone, Debug)]
pub enum Status {
Runnable,
@@ -124,13 +122,9 @@ impl PartialEq for WaitpidKey {
impl Eq for WaitpidKey {}
/// A context, which identifies either a process or a thread
/// A context, which is typically mapped to a userspace thread
#[derive(Debug)]
pub struct Context {
/// The process ID of this context
pub pid: ProcessId,
/// Process state shared with other threads
pub process: Arc<RwLock<Process>>,
/// Signal handler
pub sig: Option<SignalState>,
/// Status of context
@@ -183,6 +177,11 @@ pub struct Context {
pub userspace: bool,
pub being_sigkilled: bool,
pub fmap_ret: Option<Frame>,
// TODO: Temporary replacement for existing kernel logic, replace with capabilities!
pub is_privileged: bool,
pub ens: SchemeNamespace,
pub rns: SchemeNamespace,
}
#[derive(Debug)]
@@ -203,10 +202,8 @@ pub struct SignalState {
}
impl Context {
pub fn new(pid: ProcessId, process: Arc<RwLock<Process>>) -> Result<Context> {
pub fn new() -> Result<Context> {
let this = Context {
pid,
process,
sig: None,
status: Status::HardBlocked {
reason: HardBlockedReason::NotYetStarted,
@@ -231,6 +228,10 @@ impl Context {
fmap_ret: None,
being_sigkilled: false,
is_privileged: false,
ens: 0.into(),
rns: 0.into(),
#[cfg(feature = "syscall_debug")]
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
};
+1 -24
View File
@@ -17,10 +17,7 @@ use crate::{
syscall::error::{Error, Result},
};
use self::{
context::Kstack,
process::{Process, ProcessId, ProcessInfo},
};
use self::context::Kstack;
pub use self::{
context::{BorrowedHtBuf, Context, Status, WaitpidKey},
switch::switch,
@@ -54,9 +51,6 @@ pub mod file;
/// Memory struct - contains a set of pages for a context
pub mod memory;
/// Process handling - TODO move to userspace
pub mod process;
/// Signal handling
pub mod signal;
@@ -70,23 +64,11 @@ pub const CONTEXT_MAX_FILES: usize = 65_536;
pub use self::arch::empty_cr3;
static KMAIN_PROCESS: Once<Arc<RwLock<Process>>> = Once::new();
// Set of weak references to all contexts available for scheduling. The only strong references are
// the context file descriptors.
static CONTEXTS: RwLock<BTreeSet<ContextRef>> = RwLock::new(BTreeSet::new());
pub fn init() {
let pid = ProcessId::new(0);
let process = KMAIN_PROCESS.call_once(|| {
Arc::new(RwLock::new(Process {
info: ProcessInfo::default(),
waitpid: Arc::new(WaitMap::new()),
threads: Vec::new(),
status: process::ProcessStatus::PossiblyRunnable,
}))
});
let mut context =
Context::new(pid, Arc::clone(process)).expect("failed to create kmain context");
context.sched_affinity = LogicalCpuSet::empty();
@@ -135,10 +117,6 @@ pub fn is_current(context: &Arc<RwSpinlock<Context>>) -> bool {
.with_context(|current| Arc::ptr_eq(context, current))
}
pub fn current_pid() -> Result<ProcessId> {
Ok(current().read().pid)
}
pub struct ContextRef(pub Arc<RwSpinlock<Context>>);
impl ContextRef {
pub fn upgrade(&self) -> Option<Arc<RwSpinlock<Context>>> {
@@ -166,7 +144,6 @@ impl Eq for ContextRef {}
/// Spawn a context from a function.
pub fn spawn(
userspace_allowed: bool,
process: Arc<RwLock<Process>>,
func: extern "C" fn(),
) -> Result<Arc<RwSpinlock<Context>>> {
let stack = Kstack::new()?;
-124
View File
@@ -1,124 +0,0 @@
use core::{
ops::{Deref, DerefMut},
sync::atomic::{AtomicUsize, Ordering},
};
// TODO: move all this code to userspace
use alloc::{
collections::BTreeMap,
sync::{Arc, Weak},
vec::Vec,
};
use spin::RwLock;
use spinning_top::RwSpinlock;
use syscall::{Error, Result, ENOMEM, ESRCH};
use crate::{
scheme::{CallerCtx, SchemeNamespace},
sync::WaitMap,
};
use crate::context::{self, Context, WaitpidKey};
int_like!(ProcessId, AtomicProcessId, usize, AtomicUsize);
#[derive(Debug)]
pub struct Process {
pub info: ProcessInfo,
/// Context is being waited on
pub waitpid: Arc<WaitMap<WaitpidKey, (ProcessId, usize)>>,
pub status: ProcessStatus,
pub threads: Vec<Weak<RwSpinlock<Context>>>,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ProcessInfo {
/// The process ID of this process
pub pid: ProcessId,
/// The group ID of this process
pub pgid: ProcessId,
/// The ID of the parent process
pub ppid: ProcessId,
/// The ID of the session
pub session_id: ProcessId,
/// The real user id
pub ruid: u32,
/// The real group id
pub rgid: u32,
/// The real namespace id
pub rns: SchemeNamespace,
/// The effective user id
pub euid: u32,
/// The effective group id
pub egid: u32,
/// The effective namespace id
pub ens: SchemeNamespace,
}
impl Deref for Process {
type Target = ProcessInfo;
fn deref(&self) -> &Self::Target {
&self.info
}
}
impl DerefMut for Process {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.info
}
}
#[derive(Debug, Clone, Copy)]
pub enum ProcessStatus {
PossiblyRunnable,
Stopped(usize),
Exiting,
Exited(usize),
}
pub const INIT: ProcessId = ProcessId::new(1);
static NEXT_PID: AtomicProcessId = AtomicProcessId::new(INIT);
pub static PROCESSES: RwLock<BTreeMap<ProcessId, Arc<RwLock<Process>>>> =
RwLock::new(BTreeMap::new());
/// Get an iterator of all parents
pub fn ancestors(
list: &BTreeMap<ProcessId, Arc<RwLock<Process>>>,
id: ProcessId,
) -> impl Iterator<Item = (ProcessId, &Arc<RwLock<Process>>)> + '_ {
core::iter::successors(
list.get(&id).map(|process| (id, process)),
move |(_id, process)| {
let process = process.read();
let id = process.ppid;
list.get(&id).map(|process| (id, process))
},
)
}
pub fn current() -> Result<Arc<RwLock<Process>>> {
let pid = context::current().read().pid;
Ok(Arc::clone(
PROCESSES.read().get(&pid).ok_or(Error::new(ESRCH))?,
))
}
impl Process {
pub fn caller_ctx(&self) -> CallerCtx {
CallerCtx {
pid: self.pid.into(),
uid: self.euid,
gid: self.egid,
}
}
}
pub fn new_process(info: impl FnOnce(ProcessId) -> ProcessInfo) -> Result<Arc<RwLock<Process>>> {
let pid = NEXT_PID.fetch_add(ProcessId::new(1), Ordering::Relaxed);
let proc = Arc::try_new(RwLock::new(Process {
waitpid: Arc::try_new(WaitMap::new()).map_err(|_| Error::new(ENOMEM))?,
threads: Vec::new(),
status: ProcessStatus::PossiblyRunnable,
info: info(pid),
}))
.map_err(|_| Error::new(ENOMEM))?;
PROCESSES.write().insert(pid, Arc::clone(&proc));
Ok(proc)
}
+2 -1
View File
@@ -80,7 +80,8 @@ pub fn excp_handler(_signal: usize) {
let Some(_eh) = context.sig.as_ref().and_then(|s| s.excp_handler) else {
drop(context);
crate::syscall::process::exit(SIGKILL << 8);
// TODO: Send exception event to process manager
todo!()
};
// TODO
+4 -3
View File
@@ -255,16 +255,17 @@ pub fn switch() -> SwitchResult {
_next_guard: next_context_guard,
}));
let (ptrace_session, ptrace_flags) = if let Some((session, bp)) = ptrace::sessions()
/*let (ptrace_session, ptrace_flags) = if let Some((session, bp)) = ptrace::sessions()
.get(&next_context.pid)
.map(|s| (Arc::downgrade(s), s.data.lock().breakpoint))
{
(Some(session), bp.map_or(PtraceFlags::empty(), |f| f.flags))
} else {
(None, PtraceFlags::empty())
};
};*/
let ptrace_flags = PtraceFlags::empty();
*percpu.ptrace_session.borrow_mut() = ptrace_session;
//*percpu.ptrace_session.borrow_mut() = ptrace_session;
percpu.ptrace_flags.set(ptrace_flags);
prev_context.inside_syscall = percpu.inside_syscall.replace(next_context.inside_syscall);
+3 -25
View File
@@ -64,10 +64,7 @@ extern crate bitflags;
use core::sync::atomic::{AtomicU32, Ordering};
use crate::{
context::{
process::{new_process, ProcessInfo, INIT},
switch::SwitchResult,
},
context::switch::SwitchResult,
scheme::SchemeNamespace,
};
@@ -202,8 +199,7 @@ fn kmain(cpu_count: u32, bootstrap: Bootstrap) -> ! {
//Initialize global schemes, such as `acpi:`.
scheme::init_globals();
let pid = syscall::getpid();
info!("BSP: {:?} {}", pid, cpu_count);
info!("BSP: {}", cpu_count);
info!("Env: {:?}", ::core::str::from_utf8(bootstrap.env));
BOOTSTRAP.call_once(|| bootstrap);
@@ -211,30 +207,12 @@ fn kmain(cpu_count: u32, bootstrap: Bootstrap) -> ! {
#[cfg(feature = "profiling")]
profiling::ready_for_profiling();
let process = new_process(|_| ProcessInfo {
pid: INIT,
ppid: INIT,
pgid: INIT,
session_id: INIT,
ruid: 0,
rgid: 0,
euid: 0,
egid: 0,
rns: SchemeNamespace::new(0),
ens: SchemeNamespace::new(0),
})
.expect("failed to create init process");
match context::spawn(true, process, userspace_init) {
match context::spawn(true, userspace_init) {
Ok(context_lock) => {
{
let mut context = context_lock.write();
context.status = context::Status::Runnable;
context.name = "bootstrap".into();
let mut process = context.process.write();
process.rns = SchemeNamespace::from(1);
process.ens = SchemeNamespace::from(1);
}
INIT_THREAD.call_once(move || context_lock);
}
+1 -1
View File
@@ -97,7 +97,7 @@ impl Session {
}
}
type SessionMap = HashMap<ProcessId, Arc<Session>>;
type SessionMap = HashMap<ContextId, Arc<Session>>;
static SESSIONS: Once<RwLock<SessionMap>> = Once::new();
+16 -494
View File
@@ -5,7 +5,6 @@ use crate::{
context::{HardBlockedReason, SignalState},
file::{FileDescriptor, InternalFlags},
memory::{handle_notify_files, AddrSpaceWrapper, Grant, PageSpan},
process::{self, Process, ProcessId, ProcessInfo, ProcessStatus},
Context, Status,
},
memory::PAGE_SIZE,
@@ -17,7 +16,7 @@ use crate::{
error::*,
flag::*,
usercopy::{UserSliceRo, UserSliceWo},
EnvRegisters, FloatRegisters, IntRegisters, KillMode, KillTarget,
EnvRegisters, FloatRegisters, IntRegisters, KillMode,
},
};
@@ -97,11 +96,6 @@ enum RegsKind {
}
#[derive(Clone)]
enum ProcHandle {
Trace {
pid: ProcessId,
clones: Vec<ProcessId>,
excl: bool,
},
Static {
ty: &'static str,
bytes: Box<[u8]>,
@@ -158,10 +152,6 @@ enum Handle {
context: Arc<RwSpinlock<Context>>,
kind: ContextHandle,
},
Process {
process: Arc<RwLock<Process>>,
kind: ProcHandle,
},
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Attr {
@@ -169,65 +159,6 @@ enum Attr {
Gid,
// TODO: namespace, tid, etc.
}
impl Handle {
fn needs_child_process(&self) -> bool {
matches!(
self,
Self::Process {
kind: ProcHandle::Trace { .. } | ProcHandle::SessionId,
..
} | Self::Context {
kind: ContextHandle::Regs(_)
| ContextHandle::Filetable { .. }
| ContextHandle::NewFiletable { .. }
| ContextHandle::AddrSpace { .. }
| ContextHandle::CurrentAddrSpace
| ContextHandle::CurrentFiletable
| ContextHandle::Sighandler,
..
}
)
}
fn needs_root(&self) -> bool {
matches!(
self,
Self::Process {
kind: ProcHandle::Attr { .. },
..
}
)
}
}
impl Handle {
fn continue_ignored_children(&mut self) -> Option<()> {
let Handle::Process {
kind: ProcHandle::Trace { clones, .. },
..
} = self
else {
return None;
};
for pid in clones.drain(..) {
if ptrace::is_traced(pid) {
continue;
}
let Some(child_process) = process::PROCESSES.read().get(&pid).map(Arc::clone) else {
continue;
};
for thread in child_process
.read()
.threads
.iter()
.filter_map(|t| t.upgrade())
{
thread.write().status = context::Status::Runnable;
}
}
Some(())
}
}
pub struct ProcScheme<const FULL: bool>;
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
@@ -256,7 +187,6 @@ fn new_handle((handle, fl): (Handle, InternalFlags)) -> Result<(usize, InternalF
}
enum OpenTy {
Proc(ProcessId),
Ctxt(Arc<RwSpinlock<Context>>),
}
@@ -309,44 +239,6 @@ impl<const FULL: bool> ProcScheme<FULL> {
_ => return Ok(None),
}))
}
fn openat_process(
&self,
target: &Arc<RwLock<Process>>,
name: &str,
flags: usize,
) -> Result<Option<(ProcHandle, bool)>> {
Ok(Some(match name {
"trace" => (
ProcHandle::Trace {
pid: target.read().pid,
clones: Vec::new(),
excl: flags & O_EXCL == O_EXCL,
},
false,
),
"exe" => (
ProcHandle::Static {
ty: "exe",
// FIXME: allow opening any thread
bytes: target
.read()
.threads
.first()
.and_then(|f| f.upgrade())
.ok_or(Error::new(ESRCH))?
.read()
.name
.as_bytes()
.into(),
},
true,
),
"uid" => (ProcHandle::Attr { attr: Attr::Uid }, true),
"gid" => (ProcHandle::Attr { attr: Attr::Gid }, true),
"session_id" => (ProcHandle::SessionId, true),
_ => return Ok(None),
}))
}
fn open_inner(
&self,
ty: OpenTy,
@@ -355,42 +247,24 @@ impl<const FULL: bool> ProcScheme<FULL> {
uid: u32,
gid: u32,
) -> Result<(usize, InternalFlags)> {
let target = match ty {
OpenTy::Proc(pid) => {
let processes = process::PROCESSES.read();
Arc::clone(processes.get(&pid).ok_or(Error::new(ESRCH))?)
}
OpenTy::Ctxt(ref context) => Arc::clone(&context.read().process),
};
let operation_name = operation_str.ok_or(Error::new(EINVAL))?;
let (mut handle, positioned) = {
if let Some((kind, positioned)) = self.openat_process(&target, operation_name, flags)? {
(
Handle::Process {
process: Arc::clone(&target),
kind,
},
positioned,
)
let context = match ty {
OpenTy::Proc(_) => target
.read()
.threads
.first()
.ok_or(Error::new(ESRCH))?
.upgrade()
.ok_or(Error::new(ESRCH))?,
OpenTy::Ctxt(ref ctxt) => Arc::clone(&ctxt),
};
if let Some((kind, positioned)) =
self.openat_context(operation_name, Arc::clone(&context))?
{
(Handle::Context { context, kind }, positioned)
} else {
let context = match ty {
OpenTy::Proc(_) => target
.read()
.threads
.first()
.ok_or(Error::new(ESRCH))?
.upgrade()
.ok_or(Error::new(ESRCH))?,
OpenTy::Ctxt(ref ctxt) => Arc::clone(&ctxt),
};
if let Some((kind, positioned)) =
self.openat_context(operation_name, Arc::clone(&context))?
{
(Handle::Context { context, kind }, positioned)
} else {
return Err(Error::new(EINVAL));
}
return Err(Error::new(EINVAL));
}
};
@@ -401,36 +275,6 @@ impl<const FULL: bool> ProcScheme<FULL> {
return Err(Error::new(ESRCH));
}
// Unless root, check security
if handle.needs_child_process() && uid != 0 && gid != 0 {
let current = process::current()?;
let current = current.read();
// Are we the process?
if target.pid != current.pid {
// Do we own the process?
if uid != target.euid && gid != target.egid {
return Err(Error::new(EPERM));
}
// Is it a subprocess of us? In the future, a capability could
// bypass this check.
match process::ancestors(&*process::PROCESSES.read(), target.ppid)
.find(|&(pid, _context)| pid == current.pid)
{
Some((id, context)) => {
// Paranoid sanity check, as ptrace security holes
// wouldn't be fun
assert_eq!(id, current.pid);
assert_eq!(id, context.read().pid);
}
None => return Err(Error::new(EPERM)),
}
}
} else if handle.needs_root() && (uid != 0 || gid != 0) {
return Err(Error::new(EPERM));
}
let filetable_opt = match handle {
Handle::Context {
kind:
@@ -477,27 +321,6 @@ impl<const FULL: bool> ProcScheme<FULL> {
},
))?;
if let Handle::Process {
kind: ProcHandle::Trace { pid, .. },
..
} = handle
{
if !ptrace::try_new_session(pid, id) {
// There is no good way to handle id being occupied for nothing
// here, is there?
return Err(Error::new(EBUSY));
}
if flags & O_TRUNC == O_TRUNC {
let target = target.read();
for thread in target.threads.iter().filter_map(|t| t.upgrade()) {
thread.write().status = context::Status::HardBlocked {
reason: HardBlockedReason::PtraceStop,
};
}
}
}
Ok((id, int_fl))
}
}
@@ -505,21 +328,6 @@ impl<const FULL: bool> ProcScheme<FULL> {
impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
fn kopen(&self, path: &str, flags: usize, ctx: CallerCtx) -> Result<OpenResult> {
let mut parts = path.splitn(2, '/');
let pid_str = parts.next().ok_or(Error::new(ENOENT))?;
let pid = if pid_str == "current" {
OpenTy::Ctxt(context::current())
} else if pid_str == "new" || pid_str == "new-child" {
OpenTy::Ctxt(new_child()?)
} else if pid_str == "new-thread" {
OpenTy::Ctxt(new_thread()?)
} else if !FULL {
return Err(Error::new(EACCES));
} else {
OpenTy::Proc(ProcessId::new(
pid_str.parse().map_err(|_| Error::new(ENOENT))?,
))
};
self.open_inner(pid, parts.next(), flags, ctx.uid, ctx.gid)
.map(|(r, fl)| OpenResult::SchemeLocal(r, fl))
@@ -530,19 +338,12 @@ impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
match handle {
Handle::Process {
kind: ProcHandle::Trace { pid, .. },
process: _,
} => ptrace::Session::with_session(*pid, |session| {
Ok(session.data.lock().session_fevent_flags())
}),
_ => Ok(EventFlags::empty()),
}
}
fn close(&self, id: usize) -> Result<()> {
let mut handle = HANDLES.write().remove(&id).ok_or(Error::new(EBADF))?;
handle.continue_ignored_children();
match handle {
Handle::Context {
@@ -577,26 +378,6 @@ impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
} => {
context.write().files = new_ft;
}
Handle::Process {
kind: ProcHandle::Trace { pid, excl, .. },
process,
} => {
ptrace::close_session(pid);
if excl {
syscall::kill(pid, SIGKILL, KillMode::Idempotent)?;
}
let threads = process.read().threads.clone();
for thread in threads {
let Some(context) = thread.upgrade() else {
continue;
};
let mut context = context.write();
context.status = context::Status::Runnable;
}
}
_ => (),
}
Ok(())
@@ -694,9 +475,6 @@ impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
match handle {
Handle::Context { context, kind } => kind.kreadoff(id, context, buf, offset),
Handle::Process { process, kind } => {
kind.kreadoff(id, process, buf, offset, read_flags)
}
}
}
fn kwriteoff(
@@ -713,12 +491,10 @@ impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
let handle = {
let mut handles = HANDLES.write();
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
handle.continue_ignored_children();
handle.clone()
};
match handle {
Handle::Process { process, kind } => kind.kwriteoff(process, buf),
Handle::Context { context, kind } => kind.kwriteoff(id, context, buf),
}
}
@@ -727,21 +503,6 @@ impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
let path = match handle {
Handle::Process { process, kind } => format!(
"proc:{}/{}",
process.read().pid.get(),
match kind {
ProcHandle::Attr {
attr: Attr::Uid, ..
} => "uid",
ProcHandle::Attr {
attr: Attr::Gid, ..
} => "gid",
ProcHandle::Trace { .. } => "trace",
ProcHandle::Static { ty, .. } => ty,
ProcHandle::SessionId => "session_id",
},
),
Handle::Context { context, kind } => format!(
"proc:{}/{}",
context.read().pid.get(),
@@ -818,17 +579,12 @@ impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
kind: ContextHandle::OpenViaDup,
context,
} => {
let (uid, gid) = match &*process::current()?.read() {
process => (process.euid, process.egid),
};
return self
.open_inner(
OpenTy::Ctxt(context),
Some(core::str::from_utf8(buf).map_err(|_| Error::new(EINVAL))?)
.filter(|s| !s.is_empty()),
O_RDWR | O_CLOEXEC,
uid,
gid,
)
.map(|(r, fl)| OpenResult::SchemeLocal(r, fl));
}
@@ -922,38 +678,6 @@ extern "C" fn clone_handler() {
// usermode.
}
fn new_thread() -> Result<Arc<RwSpinlock<Context>>> {
let current_process = process::current()?;
context::spawn(true, current_process, clone_handler)
}
fn new_child() -> Result<Arc<RwSpinlock<Context>>> {
let new_context = {
let current_process_info = process::current()?.read().info;
let new_process = process::new_process(|new_pid| ProcessInfo {
pid: new_pid,
ppid: current_process_info.pid,
..current_process_info
})?;
context::spawn(true, new_process, clone_handler)?
};
if ptrace::send_event(crate::syscall::ptrace_event!(
PTRACE_EVENT_CLONE,
new_context.read().pid.into()
))
.is_some()
{
// Freeze the clone, allow ptrace to put breakpoints
// to it before it starts
let mut context = new_context.write();
context.status = context::Status::HardBlocked {
reason: HardBlockedReason::PtraceStop,
};
}
Ok(new_context)
}
fn extract_scheme_number(fd: usize) -> Result<(KernelSchemes, usize)> {
let (scheme_id, number) = match &*context::current()
.read()
@@ -983,10 +707,6 @@ fn verify_scheme(scheme: KernelSchemes) -> Result<()> {
impl Handle {
fn fsize(&self) -> Result<u64> {
match self {
Self::Process {
kind: ProcHandle::Static { ref bytes, .. },
..
} => Ok(bytes.len() as u64),
Self::Context {
kind:
ContextHandle::Filetable { ref data, .. }
@@ -997,183 +717,6 @@ impl Handle {
}
}
}
impl ProcHandle {
fn kwriteoff(self, process: Arc<RwLock<Process>>, buf: UserSliceRo) -> Result<usize> {
match self {
Self::Static { .. } => Err(Error::new(EBADF)),
Self::Trace { pid, .. } => {
let op = buf.read_u64()?;
let op = PtraceFlags::from_bits(op).ok_or(Error::new(EINVAL))?;
// Set next breakpoint
ptrace::Session::with_session(pid, |session| {
session.data.lock().set_breakpoint(
Some(op).filter(|op| op.intersects(PTRACE_STOP_MASK | PTRACE_EVENT_MASK)),
);
Ok(())
})?;
let first = process
.read()
.threads
.first()
.and_then(|f| f.upgrade())
.ok_or(Error::new(ESRCH))?;
if op.contains(PTRACE_STOP_SINGLESTEP) {
try_stop_context(first, |context| match context.regs_mut() {
None => {
println!(
"{}:{}: Couldn't read registers from stopped process",
file!(),
line!()
);
Err(Error::new(ENOTRECOVERABLE))
}
Some(stack) => {
stack.set_singlestep(true);
Ok(())
}
})?;
}
// disable the ptrace_stop flag, which is used in some cases
for thread in process.read().threads.iter().filter_map(|t| t.upgrade()) {
thread.write().status = context::Status::HardBlocked {
reason: HardBlockedReason::PtraceStop,
};
}
// and notify the tracee's WaitCondition, which is used in other cases
ptrace::Session::with_session(pid, |session| {
session.tracee.notify();
Ok(())
})?;
Ok(mem::size_of::<u64>())
}
Self::Attr { attr } => {
// TODO: What limit?
let mut str_buf = [0_u8; 32];
let bytes_copied = buf.copy_common_bytes_to_slice(&mut str_buf)?;
let id = core::str::from_utf8(&str_buf[..bytes_copied])
.map_err(|_| Error::new(EINVAL))?
.parse::<u32>()
.map_err(|_| Error::new(EINVAL))?;
match attr {
Attr::Uid => process.write().euid = id,
Attr::Gid => process.write().egid = id,
}
Ok(buf.len())
}
Self::SessionId => {
let session_id = ProcessId::new(buf.read_usize()?);
if session_id != process.read().pid {
// Session ID can only be set to this process's ID
return Err(Error::new(EPERM));
}
for (_pid, process_lock) in process::PROCESSES.read().iter() {
if session_id == process_lock.read().pgid {
// The session ID cannot match the PGID of any process
return Err(Error::new(EPERM));
}
}
{
let mut process = process.write();
process.pgid = session_id;
process.session_id = session_id;
}
Ok(buf.len())
}
}
}
fn kreadoff(
self,
id: usize,
process: Arc<RwLock<Process>>,
buf: UserSliceWo,
offset: u64,
read_flags: u32,
) -> Result<usize> {
match self {
Self::Static { bytes, .. } => read_from(buf, &bytes, offset),
Self::Trace { pid, .. } => {
// Wait for event
if (read_flags as usize) & O_NONBLOCK != O_NONBLOCK {
ptrace::wait(pid)?;
}
// Check if process exists
let _ = process::PROCESSES
.read()
.get(&pid)
.ok_or(Error::new(ESRCH))?;
let mut src_buf = [PtraceEvent::default(); 4];
// Read events
let src_len = src_buf.len();
let slice = &mut src_buf
[..core::cmp::min(src_len, buf.len() / mem::size_of::<PtraceEvent>())];
let (read, reached) = ptrace::Session::with_session(pid, |session| {
let mut data = session.data.lock();
Ok((data.recv_events(slice), data.is_reached()))
})?;
let mut handles = HANDLES.write();
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
let Handle::Process {
kind: ProcHandle::Trace { ref mut clones, .. },
..
} = handle
else {
return Err(Error::new(EBADFD));
};
// Save child processes in a list of processes to restart
for event in &slice[..read] {
if event.cause == PTRACE_EVENT_CLONE {
clones.push(ProcessId::from(event.a));
}
}
// If there are no events, and breakpoint isn't reached, we
// must not have waited.
if read == 0 && !reached {
return Err(Error::new(EAGAIN));
}
for (dst, src) in buf
.in_exact_chunks(mem::size_of::<PtraceEvent>())
.zip(slice.iter())
{
dst.copy_exactly(src)?;
}
// Return read events
Ok(read * mem::size_of::<PtraceEvent>())
}
Self::SessionId => {
read_from(buf, &process.read().session_id.get().to_ne_bytes(), offset)
}
Self::Attr { attr } => {
let src_buf = match (attr, process.read()) {
(Attr::Uid, process) => process.euid.to_string(),
(Attr::Gid, process) => process.egid.to_string(),
}
.into_bytes();
read_from(buf, &src_buf, offset)
}
}
}
}
impl ContextHandle {
fn kwriteoff(
self,
@@ -1357,7 +900,6 @@ impl ContextHandle {
return Err(Error::new(EBADF));
};
let filetable = match *entry.get_mut() {
Handle::Process { .. } => return Err(Error::new(EBADF)),
Handle::Context {
kind: ContextHandle::Filetable { ref filetable, .. },
..
@@ -1445,18 +987,6 @@ impl ContextHandle {
}
let is_current = context::is_current(&context);
{
let process = Arc::clone(&context.read().process);
let mut process = process.write();
if let Some(pos) = process
.threads
.iter()
.position(|p| Weak::as_ptr(p) == Arc::as_ptr(&context))
{
process.threads.remove(pos);
}
}
if is_current {
crate::syscall::exit_this_context();
} else {
@@ -1487,14 +1017,6 @@ impl ContextHandle {
}
}
ContextHandle::Signal => {
let me = {
let p = process::current()?;
let p = p.read();
SenderInfo {
pid: p.pid.get().try_into().unwrap_or(0),
ruid: p.ruid,
}
};
let sig = buf.read_u32()?;
let mut killed_self = false;
crate::syscall::process::send_signal(
+3 -2
View File
@@ -1,9 +1,10 @@
use alloc::vec::Vec;
use crate::{context::process, scheme, syscall::error::Result};
use crate::context;
use crate::{scheme, syscall::error::Result};
pub fn resource() -> Result<Vec<u8>> {
let scheme_ns = process::current()?.read().ens;
let scheme_ns = context::current().read().ens;
let mut data = Vec::new();
-22
View File
@@ -194,12 +194,7 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> us
clock_gettime(b, UserSlice::wo(c, core::mem::size_of::<TimeSpec>())?).map(|()| 0)
}
SYS_FUTEX => futex(b, c, d, e, f),
SYS_GETPID => getpid().map(ProcessId::into),
SYS_GETPGID => getpgid(ProcessId::from(b)).map(ProcessId::into),
SYS_GETPPID => getppid().map(ProcessId::into),
SYS_EXIT => exit(b),
SYS_KILL => kill(ProcessId::from(b), c, KillMode::Idempotent),
SYS_SIGENQUEUE => kill(
ProcessId::from(b),
c,
@@ -210,33 +205,16 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> us
SYS_SIGDEQUEUE => {
sigdequeue(UserSlice::wo(b, size_of::<RtSigInfo>())?, c as u32).map(|()| 0)
}
SYS_WAITPID => waitpid(
ProcessId::from(b),
if c == 0 {
None
} else {
Some(UserSlice::wo(c, core::mem::size_of::<usize>())?)
},
WaitFlags::from_bits_truncate(d),
)
.map(ProcessId::into),
SYS_IOPL => iopl(b),
SYS_GETEGID => getegid(),
SYS_GETENS => getens(),
SYS_GETEUID => geteuid(),
SYS_GETGID => getgid(),
SYS_GETNS => getns(),
SYS_GETUID => getuid(),
SYS_MPROTECT => mprotect(b, c, MapFlags::from_bits_truncate(d)).map(|()| 0),
SYS_MKNS => mkns(UserSlice::ro(
b,
c.checked_mul(core::mem::size_of::<[usize; 2]>())
.ok_or(Error::new(EOVERFLOW))?,
)?),
SYS_SETPGID => setpgid(ProcessId::from(b), ProcessId::from(c)).map(|()| 0),
SYS_SETREUID => setreuid(b as u32, c as u32).map(|()| 0),
SYS_SETRENS => setrens(SchemeNamespace::from(b), SchemeNamespace::from(c)).map(|()| 0),
SYS_SETREGID => setregid(b as u32, c as u32).map(|()| 0),
SYS_VIRTTOPHYS => virttophys(b),
SYS_MREMAP => mremap(b, c, d, e, f),
+2 -489
View File
@@ -10,7 +10,6 @@ use spin::RwLock;
use crate::context::{
memory::{AddrSpace, Grant, PageSpan},
process::{self, Process, ProcessId, ProcessInfo, ProcessStatus},
Context, ContextRef, WaitpidKey,
};
@@ -20,10 +19,7 @@ use crate::{
ptrace,
syscall::{
error::*,
flag::{
wifcontinued, wifstopped, MapFlags, WaitFlags, PTRACE_STOP_EXIT, SIGCONT, WCONTINUED,
WNOHANG, WUNTRACED,
},
flag::MapFlags,
ptrace_event,
},
Bootstrap, CurrentRmmArch,
@@ -72,133 +68,8 @@ pub fn wait_for_exit(context_lock: Arc<RwSpinlock<Context>>) {
}
}
pub fn exit(status: usize) -> ! {
if matches!(
mem::replace(
&mut process::current().unwrap().write().status,
ProcessStatus::Exiting
),
ProcessStatus::Exiting
) {
// already exiting the current process, so just set our status to Dead and context switch.
exit_this_context();
}
ptrace::breakpoint_callback(
PTRACE_STOP_EXIT,
Some(ptrace_event!(PTRACE_STOP_EXIT, status)),
);
let current_pid;
let current_ruid;
{
let current_context = context::current();
let current_process = process::current().expect("no active process during exit syscall");
current_pid = current_process.read().pid;
let threads = core::mem::take(&mut current_process.write().threads);
for context_lock in threads.into_iter().filter_map(|t| t.upgrade()) {
// Current context must be closed last, as it would otherwise be impossible to context
// switch back, if closing file descriptors require scheme calls.
if Arc::ptr_eq(&context_lock, &current_context) {
continue;
}
wait_for_exit(context_lock);
}
{
// PGID and PPID must be grabbed after close, as context switches could change PGID or PPID if parent exits
let (pgid, ppid) = {
let process = current_process.read();
current_ruid = process.ruid;
(process.pgid, process.ppid)
};
if let Some(parent) = process::PROCESSES.read().get(&ppid).map(Arc::clone) {
let _ = send_signal(
KillTarget::Process(parent),
SIGCHLD,
KillMode::Idempotent,
true,
&mut false,
SenderInfo {
pid: current_pid.get().try_into().unwrap_or(0),
ruid: current_ruid,
},
);
}
// Transfer child processes to parent (TODO: to init)
{
let processes = context::process::PROCESSES.read();
for (_child_pid, child_process_lock) in processes.iter() {
let mut process = child_process_lock.write();
if process.ppid == current_pid {
process.ppid = ppid;
}
}
}
current_process.write().status = ProcessStatus::Exited(status);
let children = current_process.write().waitpid.receive_all();
{
let processes = process::PROCESSES.read();
if let Some(parent_lock) = processes.get(&ppid) {
let waitpid = Arc::clone(&parent_lock.write().waitpid);
for (c_pid, c_status) in children {
waitpid.send(c_pid, c_status);
}
waitpid.send(
WaitpidKey {
pid: Some(current_pid),
pgid: Some(pgid),
},
(current_pid, status),
);
}
}
// Alert any tracers waiting of this process
ptrace::close_tracee(current_pid);
}
}
exit_this_context();
}
pub fn getpid() -> Result<ProcessId> {
context::current_pid()
}
pub fn getpgid(pid: ProcessId) -> Result<ProcessId> {
let process_lock = if pid.get() == 0 {
process::current()?
} else {
Arc::clone(
process::PROCESSES
.read()
.get(&pid)
.ok_or(Error::new(ESRCH))?,
)
};
let process = process_lock.read();
Ok(process.pgid)
}
pub fn getppid() -> Result<ProcessId> {
Ok(process::current()?.read().ppid)
}
pub enum KillTarget {
Process(Arc<RwLock<Process>>),
Thread(Arc<RwSpinlock<Context>>),
}
pub fn send_signal(
target: KillTarget,
context: Arc<RwLock<Context>>,
sig: usize,
mode: KillMode,
is_sigchld_to_parent: bool,
@@ -229,13 +100,9 @@ pub fn send_signal(
enum SendResult {
Succeeded,
SucceededSigchld {
ppid: ProcessId,
pgid: ProcessId,
orig_signal: usize,
},
SucceededSigcont {
ppid: ProcessId,
pgid: ProcessId,
},
FullQ,
Invalid,
@@ -290,8 +157,6 @@ pub fn send_signal(
}
// POSIX XSI allows but does not reqiure SIGCHLD to be sent when SIGCONT occurs.
return SendResult::SucceededSigcont {
ppid: proc_info.ppid,
pgid: proc_info.pgid,
};
}
drop(process_guard);
@@ -325,8 +190,6 @@ pub fn send_signal(
}
return SendResult::SucceededSigchld {
ppid: proc_info.ppid,
pgid: proc_info.pgid,
orig_signal: sig,
};
}
@@ -429,42 +292,8 @@ pub fn send_signal(
pgid,
orig_signal,
} => {
let parent = process::PROCESSES
.read()
.get(&ppid)
.map(Arc::clone)
.ok_or(Error::new(ESRCH))?;
let waitpid = Arc::clone(&parent.read().waitpid);
waitpid.send(
WaitpidKey {
pid: Some(proc_info.pid),
pgid: Some(pgid),
},
(proc_info.pid, (orig_signal << 8) | 0x7f),
);
send_signal(
KillTarget::Process(parent),
SIGCHLD,
mode,
true,
killed_self,
sender,
)?;
}
SendResult::SucceededSigcont { ppid, pgid } => {
let parent = process::PROCESSES
.read()
.get(&ppid)
.map(Arc::clone)
.ok_or(Error::new(ESRCH))?;
let waitpid = Arc::clone(&parent.read().waitpid);
waitpid.send(
WaitpidKey {
pid: Some(proc_info.pid),
pgid: Some(pgid),
},
(proc_info.pid, 0xffff),
);
// POSIX XSI allows but does not require SIGCONT to send signals to the parent.
//send_signal(KillTarget::Process(parent), SIGCHLD, true, killed_self)?;
}
@@ -479,106 +308,6 @@ pub enum KillMode {
Queued(RtSigInfo),
}
pub fn kill(pid: ProcessId, sig: usize, mode: KillMode) -> Result<usize> {
let (current_ruid, current_euid, current_pgid, current_pid) = {
let process_lock = process::current()?;
let process = process_lock.read();
(process.ruid, process.euid, process.pgid, process.pid)
};
let sender = SenderInfo {
pid: current_pid.get().try_into().unwrap_or(0),
ruid: current_ruid,
};
let mut found = 0;
let mut sent = 0;
let mut killed_self = false;
// Non-root users cannot kill arbitrarily.
let can_send = |proc_info: &ProcessInfo| {
current_euid == 0 || current_euid == proc_info.ruid || current_ruid == proc_info.ruid
};
{
let processes = process::PROCESSES.read();
if pid.get() as isize > 0 {
// Send to a single process
if let Some(process_lock) = processes.get(&pid).map(Arc::clone) {
found += 1;
if can_send(&process_lock.read().info) {
sent += 1;
send_signal(
KillTarget::Process(process_lock),
sig,
mode,
false,
&mut killed_self,
sender,
)?;
}
}
} else if pid.get() == 1_usize.wrapping_neg() {
// Send to every process with permission, except for init
for (pid, process_lock) in processes.iter() {
if pid.get() <= 1 {
continue;
}
found += 1;
if can_send(&process_lock.read().info) {
sent += 1;
send_signal(
KillTarget::Process(Arc::clone(process_lock)),
sig,
mode,
false,
&mut killed_self,
sender,
)?;
}
}
} else {
let pgid = if pid.get() == 0 {
current_pgid
} else {
ProcessId::from(pid.get().wrapping_neg())
};
// Send to every process in the process group whose ID
for (_pid, process_lock) in processes.iter() {
if process_lock.read().pgid != pgid {
continue;
}
found += 1;
if can_send(&process_lock.read().info) {
sent += 1;
send_signal(
KillTarget::Process(Arc::clone(process_lock)),
sig,
mode,
false,
&mut killed_self,
sender,
)?;
}
}
}
}
if found == 0 {
Err(Error::new(ESRCH))
} else if sent == 0 {
Err(Error::new(EPERM))
} else if killed_self {
// Inform userspace it should check its own mask
Err(Error::new(EINTR))
} else {
Ok(0)
}
}
pub fn mprotect(address: usize, size: usize, flags: MapFlags) -> Result<()> {
// println!("mprotect {:#X}, {}, {:#X}", address, size, flags);
@@ -588,222 +317,6 @@ pub fn mprotect(address: usize, size: usize, flags: MapFlags) -> Result<()> {
AddrSpace::current()?.mprotect(span, flags)
}
pub fn setpgid(pid: ProcessId, pgid: ProcessId) -> Result<()> {
let current_pid = context::current_pid()?;
let processes = process::PROCESSES.read();
let process_lock = if pid.get() == 0 {
process::current()?
} else {
Arc::clone(processes.get(&pid).ok_or(Error::new(ESRCH))?)
};
let mut process = process_lock.write();
if process.pid == current_pid || process.ppid == current_pid {
if pgid.get() == 0 {
process.pgid = process.pid;
} else {
process.pgid = pgid;
}
Ok(())
} else {
Err(Error::new(ESRCH))
}
}
fn reap(pid: ProcessId) -> Result<ProcessId> {
let process_lock = Arc::clone(
process::PROCESSES
.read()
.get(&pid)
.ok_or(Error::new(ESRCH))?,
);
// Spin until not running
loop {
// TODO: exit WaitCondition?
{
let process = process_lock.read();
if process
.threads
.iter()
.all(|t| t.upgrade().map_or(true, |t| !t.read().running))
{
break;
}
}
// TODO: context switch?
interrupt::pause();
}
let _ = process::PROCESSES
.write()
.remove(&pid)
.ok_or(Error::new(ESRCH))?;
Ok(pid)
}
pub fn waitpid(
pid: ProcessId,
status_ptr: Option<UserSliceWo>,
flags: WaitFlags,
) -> Result<ProcessId> {
let (ppid, waitpid) = {
let process_lock = process::current()?;
let process = process_lock.read();
(process.pid, Arc::clone(&process.waitpid))
};
let write_status = |value| {
status_ptr
.map(|ptr| ptr.write_usize(value))
.unwrap_or(Ok(()))
};
let grim_reaper = |w_pid: ProcessId, status: usize| -> Option<Result<ProcessId>> {
if wifcontinued(status) {
if flags & WCONTINUED == WCONTINUED {
Some(write_status(status).map(|()| w_pid))
} else {
None
}
} else if wifstopped(status) {
if flags & WUNTRACED == WUNTRACED {
Some(write_status(status).map(|()| w_pid))
} else {
None
}
} else {
Some(write_status(status).and_then(|()| reap(w_pid)))
}
};
loop {
let res_opt = if pid.get() == 0 {
// Check for existence of child
{
let mut found = false;
let processes = process::PROCESSES.read();
for (_id, process_lock) in processes.iter() {
let process = process_lock.read();
if process.ppid == ppid {
found = true;
break;
}
}
if !found {
return Err(Error::new(ECHILD));
}
}
if flags & WNOHANG == WNOHANG {
if let Some((_wid, (w_pid, status))) = waitpid.receive_any_nonblock() {
grim_reaper(w_pid, status)
} else {
Some(Ok(ProcessId::from(0)))
}
} else {
let (_wid, (w_pid, status)) = waitpid.receive_any("waitpid any");
grim_reaper(w_pid, status)
}
} else if (pid.get() as isize) < 0 {
let pgid = ProcessId::from(-(pid.get() as isize) as usize);
// Check for existence of child in process group PGID
{
let mut found = false;
let processes = process::PROCESSES.read();
for (_pid, process_lock) in processes.iter() {
let process = process_lock.read();
if process.pgid == pgid {
found = true;
break;
}
}
if !found {
return Err(Error::new(ECHILD));
}
}
if flags & WNOHANG == WNOHANG {
if let Some((w_pid, status)) = waitpid.receive_nonblock(&WaitpidKey {
pid: None,
pgid: Some(pgid),
}) {
grim_reaper(w_pid, status)
} else {
Some(Ok(ProcessId::from(0)))
}
} else {
let (w_pid, status) = waitpid
.receive(
&WaitpidKey {
pid: None,
pgid: Some(pgid),
},
"waitpid pgid",
)
.ok_or(Error::new(EINTR))?;
grim_reaper(w_pid, status)
}
} else {
let status = {
let process_lock = Arc::clone(
process::PROCESSES
.read()
.get(&pid)
.ok_or(Error::new(ESRCH))?,
);
let process_guard = process_lock.read();
if process_guard.ppid != ppid {
return Err(Error::new(ECHILD));
}
process_guard.status
};
if let ProcessStatus::Exited(status) = status {
let _ = waitpid.receive_nonblock(&WaitpidKey {
pid: Some(pid),
pgid: None,
});
grim_reaper(pid, status)
} else if flags & WNOHANG == WNOHANG {
let res = waitpid.receive_nonblock(&WaitpidKey {
pid: Some(pid),
pgid: None,
});
if let Some((w_pid, status)) = res {
grim_reaper(w_pid, status)
} else {
Some(Ok(ProcessId::from(0)))
}
} else {
let (w_pid, status) = waitpid
.receive(
&WaitpidKey {
pid: Some(pid),
pgid: None,
},
"waitpid pid",
)
.ok_or(Error::new(EINTR))?;
grim_reaper(w_pid, status)
}
};
if let Some(res) = res_opt {
return res;
}
}
}
pub unsafe fn usermode_bootstrap(bootstrap: &Bootstrap) {
assert_ne!(bootstrap.page_count, 0);