From cbb17327aad64621c8150736aa2fe3a34764ea96 Mon Sep 17 00:00:00 2001
From: jD91mZM2 <me@krake.one>
Date: Tue, 16 Jun 2020 13:00:27 +0200
Subject: [PATCH] ptrace: Block on read, not on write

---
 src/ptrace.rs      |  6 ++++
 src/scheme/proc.rs | 68 ++++++++++++++++++++++++----------------------
 syscall            |  2 +-
 3 files changed, 43 insertions(+), 33 deletions(-)

diff --git a/src/ptrace.rs b/src/ptrace.rs
index b55b3f6..7029d84 100644
--- a/src/ptrace.rs
+++ b/src/ptrace.rs
@@ -73,6 +73,12 @@ impl SessionData {
         });
     }
 
+    /// Returns true if the breakpoint is reached, or if there isn't a
+    /// breakpoint
+    pub fn is_reached(&self) -> bool {
+        self.breakpoint.as_ref().map(|b| b.reached).unwrap_or(false)
+    }
+
     /// Used for getting the flags in fevent
     pub fn session_fevent_flags(&self) -> EventFlags {
         let mut flags = EventFlags::empty();
diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs
index 37f640d..f80f166 100644
--- a/src/scheme/proc.rs
+++ b/src/scheme/proc.rs
@@ -365,27 +365,42 @@ impl Scheme for ProcScheme {
                 Ok(len)
             },
             Operation::Trace => {
+                let mut handles = self.handles.write();
+                let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
+                let data = handle.data.trace_data().expect("operations can't change");
+
+                // Wait for event
+                if handle.info.flags & O_NONBLOCK != O_NONBLOCK {
+                    ptrace::wait(handle.info.pid)?;
+                }
+
+                // Read events
                 let slice = unsafe {
                     slice::from_raw_parts_mut(
                         buf.as_mut_ptr() as *mut PtraceEvent,
                         buf.len() / mem::size_of::<PtraceEvent>()
                     )
                 };
-                let read = ptrace::Session::with_session(info.pid, |session| {
-                    Ok(session.data.lock().recv_events(slice))
+                let (read, reached) = ptrace::Session::with_session(info.pid, |session| {
+                    let mut data = session.data.lock();
+                    Ok((data.recv_events(slice), data.is_reached()))
                 })?;
 
-                // Won't context switch, don't worry about the locks
-                let mut handles = self.handles.write();
-                let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
-                let data = handle.data.trace_data().expect("operations can't change");
-
+                // Save child processes in a list of processes to restart
                 for event in &slice[..read] {
                     if event.cause == PTRACE_EVENT_CLONE {
                         data.clones.push(ContextId::from(event.a));
                     }
                 }
 
+                // If there are no events, and breakpoint isn't reached, we
+                // must not have waited.
+                if read == 0 && !reached {
+                    assert!(handle.info.flags & O_NONBLOCK == O_NONBLOCK, "wait woke up spuriously??");
+                    return Err(Error::new(EAGAIN));
+                }
+
+                // Return read events
                 Ok(read * mem::size_of::<PtraceEvent>())
             }
         }
@@ -470,15 +485,12 @@ impl Scheme for ProcScheme {
                 let op = u64::from_ne_bytes(bytes);
                 let op = PtraceFlags::from_bits(op).ok_or(Error::new(EINVAL))?;
 
-                let should_continue = !op.contains(PTRACE_FLAG_WAIT) || op.intersects(PTRACE_STOP_MASK);
-
                 // Set next breakpoint
                 ptrace::Session::with_session(info.pid, |session| {
-                    if op.intersects(PTRACE_STOP_MASK) {
-                        session.data.lock().set_breakpoint(Some(op));
-                    } else if should_continue {
-                        session.data.lock().set_breakpoint(None);
-                    }
+                    session.data.lock().set_breakpoint(
+                        Some(op)
+                            .filter(|op| op.intersects(PTRACE_STOP_MASK | PTRACE_EVENT_MASK))
+                    );
                     Ok(())
                 })?;
 
@@ -497,25 +509,17 @@ impl Scheme for ProcScheme {
                     })?;
                 }
 
-                // Continue execution, if requested
-                if should_continue {
-                    // disable the ptrace_stop flag, which is used in some cases
-                    with_context_mut(info.pid, |context| {
-                        context.ptrace_stop = false;
-                        Ok(())
-                    })?;
-
-                    // and notify the tracee's WaitCondition, which is used in other cases
-                    ptrace::Session::with_session(info.pid, |session| {
-                        session.tracee.notify();
-                        Ok(())
-                    })?;
-                }
+                // disable the ptrace_stop flag, which is used in some cases
+                with_context_mut(info.pid, |context| {
+                    context.ptrace_stop = false;
+                    Ok(())
+                })?;
 
-                // And await the tracee, if requested
-                if op.contains(PTRACE_FLAG_WAIT) || info.flags & O_NONBLOCK != O_NONBLOCK {
-                    ptrace::wait(info.pid)?;
-                }
+                // and notify the tracee's WaitCondition, which is used in other cases
+                ptrace::Session::with_session(info.pid, |session| {
+                    session.tracee.notify();
+                    Ok(())
+                })?;
 
                 Ok(mem::size_of::<u64>())
             }
diff --git a/syscall b/syscall
index 6ba71e7..1c637e7 160000
--- a/syscall
+++ b/syscall
@@ -1 +1 @@
-Subproject commit 6ba71e7e065d37309b7d5c4fc86835dd350f61b7
+Subproject commit 1c637e72b2f3be8e8f942372e8414101e463df98
-- 
GitLab