From be867ae5f13e0b72a9ebd0eae32b5f9e0509e175 Mon Sep 17 00:00:00 2001
From: jD91mZM2 <me@krake.one>
Date: Fri, 19 Jul 2019 17:21:21 +0200
Subject: [PATCH] WIP(ptrace): Add ptrace event system for catching child forks

---
 src/ptrace.rs          | 243 ++++++++++++++++++++++++++++++++---------
 src/scheme/mod.rs      |   6 +-
 src/scheme/proc.rs     | 155 +++++++++++++++++---------
 src/syscall/process.rs |  26 ++++-
 syscall                |   2 +-
 5 files changed, 321 insertions(+), 111 deletions(-)

diff --git a/src/ptrace.rs b/src/ptrace.rs
index 5a88d3a7..f8274968 100644
--- a/src/ptrace.rs
+++ b/src/ptrace.rs
@@ -10,17 +10,150 @@ use crate::{
     },
     common::unique::Unique,
     context::{self, Context, ContextId, Status},
+    event,
+    scheme::proc,
     sync::WaitCondition
 };
 
 use alloc::{
     boxed::Box,
-    collections::BTreeMap,
+    collections::{
+        BTreeMap,
+        VecDeque,
+        btree_map::Entry
+    },
     sync::Arc,
     vec::Vec
 };
+use core::{
+    cmp,
+    sync::atomic::Ordering
+};
 use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
-use syscall::error::*;
+use syscall::{
+    data::PtraceEvent,
+    error::*,
+    flag::{EVENT_READ, EVENT_WRITE}
+};
+
+//  ____                _
+// / ___|  ___  ___ ___(_) ___  _ __  ___
+// \___ \ / _ \/ __/ __| |/ _ \| '_ \/ __|
+//  ___) |  __/\__ \__ \ | (_) | | | \__ \
+// |____/ \___||___/___/_|\___/|_| |_|___/
+
+#[derive(Debug)]
+struct Session {
+    file_id: usize,
+    events: VecDeque<PtraceEvent>,
+    breakpoint: Option<Breakpoint>,
+    tracer: Arc<WaitCondition>
+}
+
+type SessionMap = BTreeMap<ContextId, Session>;
+
+static SESSIONS: Once<RwLock<SessionMap>> = Once::new();
+
+fn init_sessions() -> RwLock<SessionMap> {
+    RwLock::new(BTreeMap::new())
+}
+fn sessions() -> RwLockReadGuard<'static, SessionMap> {
+    SESSIONS.call_once(init_sessions).read()
+}
+fn sessions_mut() -> RwLockWriteGuard<'static, SessionMap> {
+    SESSIONS.call_once(init_sessions).write()
+}
+
+/// Try to create a new session, but fail if one already exists for
+/// this process
+pub fn try_new_session(pid: ContextId, file_id: usize) -> bool {
+    let mut sessions = sessions_mut();
+
+    match sessions.entry(pid) {
+        Entry::Occupied(_) => false,
+        Entry::Vacant(vacant) => {
+            vacant.insert(Session {
+                file_id,
+                events: VecDeque::new(),
+                breakpoint: None,
+                tracer: Arc::new(WaitCondition::new())
+            });
+            true
+        }
+    }
+}
+
+/// Returns true if a session is attached to this process
+pub fn is_traced(pid: ContextId) -> bool {
+    sessions().contains_key(&pid)
+}
+
+/// Used for getting the flags in fevent
+pub fn session_fevent_flags(pid: ContextId) -> Option<usize> {
+    let sessions = sessions();
+    let session = sessions.get(&pid)?;
+    let mut flags = 0;
+    if !session.events.is_empty() {
+        flags |= EVENT_READ;
+    }
+    if session.breakpoint.as_ref().map(|b| b.reached).unwrap_or(true) {
+        flags |= EVENT_WRITE;
+    }
+    Some(flags)
+}
+
+/// Remove the session from the list of open sessions and notify any
+/// waiting processes
+pub fn close_session(pid: ContextId) {
+    if let Some(session) = sessions_mut().remove(&pid) {
+        session.tracer.notify();
+        if let Some(breakpoint) = session.breakpoint {
+            breakpoint.tracee.notify();
+        }
+    }
+}
+
+/// Trigger a notification to the event: scheme
+pub fn proc_trigger_event(file_id: usize, flags: usize) {
+    event::trigger(proc::PROC_SCHEME_ID.load(Ordering::SeqCst), file_id, flags);
+}
+
+/// Dispatch an event to any tracer tracing `self`. This will cause
+/// the tracer to wake up and poll for events. Returns Some(()) if an
+/// event was sent.
+pub fn send_event(event: PtraceEvent) -> Option<()> {
+    let contexts = context::contexts();
+    let context = contexts.current()?;
+    let context = context.read();
+
+    let mut sessions = sessions_mut();
+    let session = sessions.get_mut(&context.id)?;
+
+    session.events.push_back(event);
+
+    // Notify nonblocking tracers
+    if session.events.len() == 1 {
+        // If the list of events was previously empty, alert now
+        proc_trigger_event(session.file_id, EVENT_READ);
+    }
+
+    // Alert blocking tracers
+    session.tracer.notify();
+
+    Some(())
+}
+
+/// Poll events, return the amount read
+pub fn recv_events(pid: ContextId, out: &mut [PtraceEvent]) -> Option<usize> {
+    let mut sessions = sessions_mut();
+    let session = sessions.get_mut(&pid)?;
+
+    let len = cmp::min(out.len(), session.events.len());
+    for (dst, src) in out.iter_mut().zip(session.events.drain(..len)) {
+        *dst = src;
+    }
+    Some(len)
+}
 
 //  ____                 _                _       _
 // | __ ) _ __ ___  __ _| | ___ __   ___ (_)_ __ | |_ ___
@@ -29,33 +162,25 @@ use syscall::error::*;
 // |____/|_|  \___|\__,_|_|\_\ .__/ \___/|_|_| |_|\__|___/
 //                           |_|
 
-struct Handle {
+#[derive(Debug)]
+struct Breakpoint {
     tracee: Arc<WaitCondition>,
-    tracer: Arc<WaitCondition>,
     reached: bool,
 
     sysemu: bool,
     singlestep: bool
 }
 
-static BREAKPOINTS: Once<RwLock<BTreeMap<ContextId, Handle>>> = Once::new();
-
-fn init_breakpoints() -> RwLock<BTreeMap<ContextId, Handle>> {
-    RwLock::new(BTreeMap::new())
-}
-fn breakpoints() -> RwLockReadGuard<'static, BTreeMap<ContextId, Handle>> {
-    BREAKPOINTS.call_once(init_breakpoints).read()
-}
-fn breakpoints_mut() -> RwLockWriteGuard<'static, BTreeMap<ContextId, Handle>> {
-    BREAKPOINTS.call_once(init_breakpoints).write()
-}
-
-fn inner_cont(pid: ContextId) -> Option<Handle> {
+fn inner_cont(pid: ContextId) -> Option<Breakpoint> {
     // Remove the breakpoint to both save space and also make sure any
     // yet unreached but obsolete breakpoints don't stop the program.
-    let handle = breakpoints_mut().remove(&pid)?;
-    handle.tracee.notify();
-    Some(handle)
+    let mut sessions = sessions_mut();
+    let session = sessions.get_mut(&pid)?;
+    let breakpoint = session.breakpoint.take()?;
+
+    breakpoint.tracee.notify();
+
+    Some(breakpoint)
 }
 
 /// Continue the process with the specified ID
@@ -63,44 +188,61 @@ pub fn cont(pid: ContextId) {
     inner_cont(pid);
 }
 
-/// Create a new breakpoint for the specified tracee, optionally with a sysemu flag
+/// Create a new breakpoint for the specified tracee, optionally with
+/// a sysemu flag. Panics if the session is invalid.
 pub fn set_breakpoint(pid: ContextId, sysemu: bool, singlestep: bool) {
-    let (tracee, tracer) = match inner_cont(pid) {
-        Some(breakpoint) => (breakpoint.tracee, breakpoint.tracer),
-        None => (
-            Arc::new(WaitCondition::new()),
-            Arc::new(WaitCondition::new())
-        )
-    };
+    let tracee = inner_cont(pid)
+        .map(|b| b.tracee)
+        .unwrap_or_else(|| Arc::new(WaitCondition::new()));
 
-    breakpoints_mut().insert(pid, Handle {
+    let mut sessions = sessions_mut();
+    let session = sessions.get_mut(&pid).expect("proc (set_breakpoint): invalid session");
+    session.breakpoint = Some(Breakpoint {
         tracee,
-        tracer,
         reached: false,
         sysemu,
         singlestep
     });
 }
 
-/// Wait for the tracee to stop.
-/// Note: Don't call while holding any locks, this will switch contexts
-pub fn wait_breakpoint(pid: ContextId) -> Result<()> {
-    let tracer = {
-        let breakpoints = breakpoints();
-        match breakpoints.get(&pid) {
-            Some(breakpoint) if !breakpoint.reached => Arc::clone(&breakpoint.tracer),
-            _ => return Ok(())
+/// Wait for the tracee to stop. If an event occurs, it returns a copy
+/// of that. It will still be available for read using recv_event.
+///
+/// Note: Don't call while holding any locks, this will switch
+/// contexts
+pub fn wait(pid: ContextId) -> Result<Option<PtraceEvent>> {
+    let tracer: Arc<WaitCondition> = {
+        let sessions = sessions();
+        match sessions.get(&pid) {
+            Some(session) if session.breakpoint.as_ref().map(|b| !b.reached).unwrap_or(true) => {
+                if let Some(event) = session.events.front() {
+                    return Ok(Some(event.clone()));
+                }
+                Arc::clone(&session.tracer)
+            },
+            _ => return Ok(None)
         }
     };
+
     while !tracer.wait() {}
 
+    {
+        let sessions = sessions();
+        if let Some(session) = sessions.get(&pid) {
+            if let Some(event) = session.events.front() {
+                return Ok(Some(event.clone()));
+            }
+        }
+    }
+
     let contexts = context::contexts();
     let context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
     let context = context.read();
     if let Status::Exited(_) = context.status {
         return Err(Error::new(ESRCH));
     }
-    Ok(())
+
+    Ok(None)
 }
 
 /// Notify the tracer and await green flag to continue.
@@ -112,8 +254,9 @@ pub fn breakpoint_callback(singlestep: bool) -> Option<bool> {
         let context = contexts.current()?;
         let context = context.read();
 
-        let mut breakpoints = breakpoints_mut();
-        let breakpoint = breakpoints.get_mut(&context.id)?;
+        let mut sessions = sessions_mut();
+        let session = sessions.get_mut(&context.id)?;
+        let breakpoint = session.breakpoint.as_mut()?;
 
         // TODO: How should singlesteps interact with syscalls? How
         // does Linux handle this?
@@ -123,11 +266,13 @@ pub fn breakpoint_callback(singlestep: bool) -> Option<bool> {
             return None;
         }
 
-        breakpoint.tracer.notify();
         // In case no tracer is waiting, make sure the next one gets
         // the memo
         breakpoint.reached = true;
 
+        session.tracer.notify();
+        proc_trigger_event(session.file_id, EVENT_WRITE);
+
         (
             Arc::clone(&breakpoint.tracee),
             breakpoint.sysemu
@@ -140,15 +285,13 @@ pub fn breakpoint_callback(singlestep: bool) -> Option<bool> {
 }
 
 /// Call when a context is closed to alert any tracers
-pub fn close(pid: ContextId) {
-    {
-        let breakpoints = breakpoints();
-        if let Some(breakpoint) = breakpoints.get(&pid) {
-            breakpoint.tracer.notify();
-        }
-    }
+pub fn close_tracee(pid: ContextId) -> Option<()> {
+    let mut sessions = sessions_mut();
+    let session = sessions.get_mut(&pid)?;
 
-    breakpoints_mut().remove(&pid);
+    session.breakpoint = None;
+    session.tracer.notify();
+    Some(())
 }
 
 //  ____            _     _
diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs
index 83becb8f..8d2e345e 100644
--- a/src/scheme/mod.rs
+++ b/src/scheme/mod.rs
@@ -96,7 +96,7 @@ impl<'a> Iterator for SchemeIter<'a> {
 
 /// Scheme list type
 pub struct SchemeList {
-    map: BTreeMap<SchemeId, Arc<Box<Scheme + Send + Sync>>>,
+    map: BTreeMap<SchemeId, Arc<Box<dyn Scheme + Send + Sync>>>,
     names: BTreeMap<SchemeNamespace, BTreeMap<Box<[u8]>, SchemeId>>,
     next_ns: usize,
     next_id: usize
@@ -141,7 +141,7 @@ impl SchemeList {
         self.insert(ns, Box::new(*b"debug"), |scheme_id| Arc::new(Box::new(DebugScheme::new(scheme_id)))).unwrap();
         self.insert(ns, Box::new(*b"initfs"), |_| Arc::new(Box::new(InitFsScheme::new()))).unwrap();
         self.insert(ns, Box::new(*b"irq"), |scheme_id| Arc::new(Box::new(IrqScheme::new(scheme_id)))).unwrap();
-        self.insert(ns, Box::new(*b"proc"), |_| Arc::new(Box::new(ProcScheme::new()))).unwrap();
+        self.insert(ns, Box::new(*b"proc"), |scheme_id| Arc::new(Box::new(ProcScheme::new(scheme_id)))).unwrap();
 
         #[cfg(feature = "live")] {
             self.insert(ns, Box::new(*b"disk/live"), |_| Arc::new(Box::new(self::live::DiskScheme::new()))).unwrap();
@@ -184,7 +184,7 @@ impl SchemeList {
     }
 
     /// Get the nth scheme.
-    pub fn get(&self, id: SchemeId) -> Option<&Arc<Box<Scheme + Send + Sync>>> {
+    pub fn get(&self, id: SchemeId) -> Option<&Arc<Box<dyn Scheme + Send + Sync>>> {
         self.map.get(&id)
     }
 
diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs
index 93fd90d8..76fc9d80 100644
--- a/src/scheme/proc.rs
+++ b/src/scheme/proc.rs
@@ -1,12 +1,13 @@
 use crate::{
     arch::paging::VirtualAddress,
     context::{self, ContextId, Status},
-    syscall::validate,
-    ptrace
+    ptrace,
+    scheme::{ATOMIC_SCHEMEID_INIT, AtomicSchemeId, SchemeId},
+    syscall::validate
 };
 
 use alloc::{
-    collections::{BTreeMap, BTreeSet},
+    collections::BTreeMap,
     sync::Arc
 };
 use core::{
@@ -17,7 +18,7 @@ use core::{
 };
 use spin::{Mutex, RwLock};
 use syscall::{
-    data::{IntRegisters, FloatRegisters},
+    data::{FloatRegisters, IntRegisters, PtraceEvent},
     error::*,
     flag::*,
     scheme::Scheme
@@ -32,7 +33,9 @@ enum RegsKind {
 enum Operation {
     Memory(VirtualAddress),
     Regs(RegsKind),
-    Trace
+    Trace {
+        new_child: Option<ContextId>
+    }
 }
 
 #[derive(Clone, Copy)]
@@ -41,19 +44,37 @@ struct Handle {
     pid: ContextId,
     operation: Operation
 }
+impl Handle {
+    fn continue_ignored_child(&mut self) -> Option<()> {
+        let pid = match self.operation {
+            Operation::Trace { ref mut new_child } => new_child.take()?,
+            _ => return None
+        };
+        if ptrace::is_traced(pid) {
+            return None;
+        }
+        let contexts = context::contexts();
+        let context = contexts.get(pid)?;
+        let mut context = context.write();
+        context.ptrace_stop = false;
+        Some(())
+    }
+}
+
+pub static PROC_SCHEME_ID: AtomicSchemeId = ATOMIC_SCHEMEID_INIT;
 
 pub struct ProcScheme {
     next_id: AtomicUsize,
-    handles: RwLock<BTreeMap<usize, Arc<Mutex<Handle>>>>,
-    traced: Mutex<BTreeSet<ContextId>>
+    handles: RwLock<BTreeMap<usize, Arc<Mutex<Handle>>>>
 }
 
 impl ProcScheme {
-    pub fn new() -> Self {
+    pub fn new(scheme_id: SchemeId) -> Self {
+        PROC_SCHEME_ID.store(scheme_id, Ordering::SeqCst);
+
         Self {
             next_id: AtomicUsize::new(0),
             handles: RwLock::new(BTreeMap::new()),
-            traced: Mutex::new(BTreeSet::new())
         }
     }
 }
@@ -70,50 +91,59 @@ impl Scheme for ProcScheme {
             Some("mem") => Operation::Memory(VirtualAddress::new(0)),
             Some("regs/float") => Operation::Regs(RegsKind::Float),
             Some("regs/int") => Operation::Regs(RegsKind::Int),
-            Some("trace") => Operation::Trace,
+            Some("trace") => Operation::Trace {
+                new_child: None
+            },
             _ => return Err(Error::new(EINVAL))
         };
 
         let contexts = context::contexts();
         let target = contexts.get(pid).ok_or(Error::new(ESRCH))?;
 
-        // Unless root, check security
-        if uid != 0 && gid != 0 {
-            let current = contexts.current().ok_or(Error::new(ESRCH))?;
-            let current = current.read();
+        {
             let target = target.read();
 
-            // Do we own the process?
-            if uid != target.euid && gid != target.egid {
-                return Err(Error::new(EPERM));
+            if let Status::Exited(_) = target.status {
+                return Err(Error::new(ESRCH));
             }
 
-            // Is it a subprocess of us? In the future, a capability
-            // could bypass this check.
-            match contexts.anchestors(target.ppid).find(|&(id, _context)| id == current.id) {
-                Some((id, context)) => {
-                    // Paranoid sanity check, as ptrace security holes
-                    // wouldn't be fun
-                    assert_eq!(id, current.id);
-                    assert_eq!(id, context.read().id);
-                },
-                None => return Err(Error::new(EPERM))
+            // Unless root, check security
+            if uid != 0 && gid != 0 {
+                let current = contexts.current().ok_or(Error::new(ESRCH))?;
+                let current = current.read();
+
+                // 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 contexts.anchestors(target.ppid).find(|&(id, _context)| id == current.id) {
+                    Some((id, context)) => {
+                        // Paranoid sanity check, as ptrace security holes
+                        // wouldn't be fun
+                        assert_eq!(id, current.id);
+                        assert_eq!(id, context.read().id);
+                    },
+                    None => return Err(Error::new(EPERM))
+                }
             }
         }
+        
+        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
 
-        if let Operation::Trace = operation {
-            let mut traced = self.traced.lock();
-
-            if traced.contains(&pid) {
+        if let Operation::Trace { .. } = operation {
+            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));
             }
-            traced.insert(pid);
 
             let mut target = target.write();
             target.ptrace_stop = true;
         }
 
-        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
         self.handles.write().insert(id, Arc::new(Mutex::new(Handle {
             flags,
             pid,
@@ -244,7 +274,16 @@ impl Scheme for ProcScheme {
 
                 Ok(len)
             },
-            Operation::Trace => Err(Error::new(EBADF))
+            Operation::Trace { .. } => {
+                let read = ptrace::recv_events(handle.pid, unsafe {
+                    slice::from_raw_parts_mut(
+                        buf.as_mut_ptr() as *mut PtraceEvent,
+                        buf.len() / mem::size_of::<PtraceEvent>()
+                    )
+                }).unwrap_or(0);
+
+                Ok(read * mem::size_of::<PtraceEvent>())
+            }
         }
     }
 
@@ -255,7 +294,11 @@ impl Scheme for ProcScheme {
             Arc::clone(handles.get(&id).ok_or(Error::new(EBADF))?)
         };
         let mut handle = handle.lock();
+        handle.continue_ignored_child();
+
+        // Some operations borrow Operation:: mutably
         let pid = handle.pid;
+        let flags = handle.flags;
 
         let mut first = true;
         match handle.operation {
@@ -320,28 +363,23 @@ impl Scheme for ProcScheme {
                     }
                 };
             },
-            Operation::Trace => {
+            Operation::Trace { ref mut new_child } => {
                 if buf.len() < 1 {
                     return Ok(0);
                 }
                 let op = buf[0];
                 let sysemu = op & PTRACE_SYSEMU == PTRACE_SYSEMU;
 
-                let mut blocking = handle.flags & O_NONBLOCK != O_NONBLOCK;
-                let mut wait_breakpoint = false;
+                let mut blocking = flags & O_NONBLOCK != O_NONBLOCK;
                 let mut singlestep = false;
 
                 match op & PTRACE_OPERATIONMASK {
-                    PTRACE_CONT => { ptrace::cont(handle.pid); },
+                    PTRACE_CONT => { ptrace::cont(pid); },
                     PTRACE_SYSCALL | PTRACE_SINGLESTEP => { // <- not a bitwise OR
                         singlestep = op & PTRACE_OPERATIONMASK == PTRACE_SINGLESTEP;
-                        ptrace::set_breakpoint(handle.pid, sysemu, singlestep);
-                        wait_breakpoint = true;
-                    },
-                    PTRACE_WAIT => {
-                        wait_breakpoint = true;
-                        blocking = true;
+                        ptrace::set_breakpoint(pid, sysemu, singlestep);
                     },
+                    PTRACE_WAIT => blocking = true,
                     _ => return Err(Error::new(EINVAL))
                 }
 
@@ -354,7 +392,7 @@ impl Scheme for ProcScheme {
                     first = false;
 
                     let contexts = context::contexts();
-                    let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?;
+                    let context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
                     let mut context = context.write();
                     if let Status::Exited(_) = context.status {
                         return Err(Error::new(ESRCH));
@@ -371,8 +409,13 @@ impl Scheme for ProcScheme {
                     break;
                 }
 
-                if wait_breakpoint && blocking {
-                    ptrace::wait_breakpoint(handle.pid)?;
+                if blocking {
+                    if let Some(event) = ptrace::wait(pid)? {
+                        if event.tag == PTRACE_EVENT_CLONE {
+                            *new_child = Some(ContextId::from(unsafe { event.data.clone }));
+                        }
+                        return Ok(0);
+                    }
                 }
 
                 Ok(1)
@@ -392,6 +435,14 @@ impl Scheme for ProcScheme {
         }
     }
 
+    fn fevent(&self, id: usize, _flags: usize) -> Result<usize> {
+        let handles = self.handles.read();
+        let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
+        let handle = handle.lock();
+
+        Ok(ptrace::session_fevent_flags(handle.pid).expect("proc (fevent): invalid session"))
+    }
+
     fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
         let handles = self.handles.read();
         let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
@@ -401,7 +452,7 @@ impl Scheme for ProcScheme {
             Operation::Memory(_) => "mem",
             Operation::Regs(RegsKind::Float) => "regs/float",
             Operation::Regs(RegsKind::Int) => "regs/int",
-            Operation::Trace => "trace"
+            Operation::Trace { .. } => "trace"
         });
 
         let len = cmp::min(path.len(), buf.len());
@@ -412,11 +463,11 @@ impl Scheme for ProcScheme {
 
     fn close(&self, id: usize) -> Result<usize> {
         let handle = self.handles.write().remove(&id).ok_or(Error::new(EBADF))?;
-        let handle = handle.lock();
+        let mut handle = handle.lock();
+        handle.continue_ignored_child();
 
-        if let Operation::Trace = handle.operation {
-            ptrace::cont(handle.pid);
-            self.traced.lock().remove(&handle.pid);
+        if let Operation::Trace { .. } = handle.operation {
+            ptrace::close_session(handle.pid);
         }
 
         let contexts = context::contexts();
diff --git a/src/syscall/process.rs b/src/syscall/process.rs
index 8165ef0e..9a74c0a2 100644
--- a/src/syscall/process.rs
+++ b/src/syscall/process.rs
@@ -21,12 +21,12 @@ use crate::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, PA
 use crate::ptrace;
 use crate::scheme::FileHandle;
 use crate::start::usermode;
-use crate::syscall::data::{SigAction, Stat};
+use crate::syscall::data::{PtraceEvent, PtraceEventContent, SigAction, Stat};
 use crate::syscall::error::*;
 use crate::syscall::flag::{CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND, CLONE_STACK,
-                    PROT_EXEC, PROT_READ, PROT_WRITE,
-                    SIG_DFL, SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK, SIGCONT, SIGTERM,
-                    WCONTINUED, WNOHANG, WUNTRACED, wifcontinued, wifstopped};
+                           PROT_EXEC, PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE,
+                           SIG_DFL, SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK, SIGCONT, SIGTERM,
+                           WCONTINUED, WNOHANG, WUNTRACED, wifcontinued, wifstopped};
 use crate::syscall::validate::{validate_slice, validate_slice_mut};
 use crate::syscall;
 
@@ -585,6 +585,22 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<ContextId> {
         }
     }
 
+    let ptrace_event = PtraceEvent {
+        tag: PTRACE_EVENT_CLONE,
+        data: PtraceEventContent {
+            clone: pid.into()
+        }
+    };
+
+    if ptrace::send_event(ptrace_event).is_some() {
+        // Freeze the clone, allow ptrace to put breakpoints
+        // to it before it starts
+        let contexts = context::contexts();
+        let context = contexts.get(pid).expect("Newly created context doesn't exist??");
+        let mut context = context.write();
+        context.ptrace_stop = true;
+    }
+
     // Race to pick up the new process!
     ipi(IpiKind::Switch, IpiTarget::Other);
 
@@ -1109,7 +1125,7 @@ pub fn exit(status: usize) -> ! {
         };
 
         // Alert any tracers waiting for process (important: AFTER sending waitpid event)
-        ptrace::close(pid);
+        ptrace::close_tracee(pid);
 
         {
             let contexts = context::contexts();
diff --git a/syscall b/syscall
index 49dd2226..eddcb80e 160000
--- a/syscall
+++ b/syscall
@@ -1 +1 @@
-Subproject commit 49dd22260bd8bada8b835d12ee8e460a5a1c4af4
+Subproject commit eddcb80eb7c2d43dedf0ba2ee514b54b0b8fafc7
-- 
GitLab