From a7da393cf569416314fd1c9e016cece352537929 Mon Sep 17 00:00:00 2001
From: jD91mZM2 <me@krake.one>
Date: Sun, 21 Jul 2019 19:58:32 +0200
Subject: [PATCH] WIP(ptrace): Better support for signals

Signals now cause an event, and there's a way to continue until the
next signal. I can see this being used for detection of `int3`
although I'm not entirely sure as it may prove being too late to stop
abortion of process.
---
 src/arch/x86_64/interrupt/syscall.rs |  3 ++-
 src/context/signal.rs                | 12 ++++++++++--
 src/ptrace.rs                        | 12 ++++++------
 src/scheme/proc.rs                   |  2 +-
 src/syscall/process.rs               | 10 +++++-----
 syscall                              |  2 +-
 6 files changed, 25 insertions(+), 16 deletions(-)

diff --git a/src/arch/x86_64/interrupt/syscall.rs b/src/arch/x86_64/interrupt/syscall.rs
index ebc3c528..6b9dfe61 100644
--- a/src/arch/x86_64/interrupt/syscall.rs
+++ b/src/arch/x86_64/interrupt/syscall.rs
@@ -21,7 +21,8 @@ macro_rules! with_interrupt_stack {
         unsafe fn $wrapped(stack: *mut InterruptStack) {
             let _guard = ptrace::set_process_regs(stack);
 
-            let is_sysemu = ptrace::breakpoint_callback(::syscall::PTRACE_SYSCALL);
+            let is_sysemu = ptrace::breakpoint_callback(syscall::flag::PTRACE_SYSCALL)
+                .map(|fl| fl & syscall::flag::PTRACE_SYSEMU == syscall::flag::PTRACE_SYSEMU);
             if !is_sysemu.unwrap_or(false) {
                 // If not on a sysemu breakpoint
                 let $stack = &mut *stack;
diff --git a/src/context/signal.rs b/src/context/signal.rs
index f7db1ae3..d5d2ff18 100644
--- a/src/context/signal.rs
+++ b/src/context/signal.rs
@@ -3,8 +3,9 @@ use core::mem;
 
 use crate::context::{contexts, switch, Status, WaitpidKey};
 use crate::start::usermode;
-use crate::syscall;
-use crate::syscall::flag::{SIG_DFL, SIG_IGN, SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU};
+use crate::{ptrace, syscall};
+use crate::syscall::flag::{PTRACE_EVENT_SIGNAL, PTRACE_SIGNAL, SIG_DFL, SIG_IGN, SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU};
+use crate::syscall::data::{PtraceEvent, PtraceEventData};
 
 pub fn is_user_handled(handler: Option<extern "C" fn(usize)>) -> bool {
     let handler = handler.map(|ptr| ptr as usize).unwrap_or(0);
@@ -20,6 +21,11 @@ pub extern "C" fn signal_handler(sig: usize) {
         actions[sig]
     };
 
+    ptrace::send_event(PtraceEvent {
+        tag: PTRACE_EVENT_SIGNAL,
+        data: PtraceEventData { signal: sig }
+    });
+
     let handler = action.sa_handler.map(|ptr| ptr as usize).unwrap_or(0);
     if handler == SIG_DFL {
         match sig {
@@ -94,6 +100,8 @@ pub extern "C" fn signal_handler(sig: usize) {
     } else {
         // println!("Call {:X}", handler);
 
+        ptrace::breakpoint_callback(PTRACE_SIGNAL);
+
         unsafe {
             let mut sp = crate::USER_SIGSTACK_OFFSET + crate::USER_SIGSTACK_SIZE - 256;
 
diff --git a/src/ptrace.rs b/src/ptrace.rs
index ae7d9b18..3f3390f4 100644
--- a/src/ptrace.rs
+++ b/src/ptrace.rs
@@ -114,7 +114,7 @@ pub fn close_session(pid: ContextId) {
 }
 
 /// Trigger a notification to the event: scheme
-pub fn proc_trigger_event(file_id: usize, flags: usize) {
+fn proc_trigger_event(file_id: usize, flags: usize) {
     event::trigger(proc::PROC_SCHEME_ID.load(Ordering::SeqCst), file_id, flags);
 }
 
@@ -244,9 +244,9 @@ pub fn wait(pid: ContextId) -> Result<Option<PtraceEvent>> {
 
 /// Notify the tracer and await green flag to continue.
 /// Note: Don't call while holding any locks, this will switch contexts
-pub fn breakpoint_callback(flags: u8) -> Option<bool> {
+pub fn breakpoint_callback(match_flags: u8) -> Option<u8> {
     // Can't hold any locks when executing wait()
-    let (tracee, sysemu) = {
+    let (tracee, flags) = {
         let contexts = context::contexts();
         let context = contexts.current()?;
         let context = context.read();
@@ -258,7 +258,7 @@ pub fn breakpoint_callback(flags: u8) -> Option<bool> {
         // TODO: How should singlesteps interact with syscalls? How
         // does Linux handle this?
 
-        if breakpoint.flags & PTRACE_OPERATIONMASK != flags & PTRACE_OPERATIONMASK {
+        if breakpoint.flags & PTRACE_OPERATIONMASK != match_flags & PTRACE_OPERATIONMASK {
             return None;
         }
 
@@ -271,13 +271,13 @@ pub fn breakpoint_callback(flags: u8) -> Option<bool> {
 
         (
             Arc::clone(&breakpoint.tracee),
-            breakpoint.flags & PTRACE_SYSEMU == PTRACE_SYSEMU
+            breakpoint.flags
         )
     };
 
     while !tracee.wait() {}
 
-    Some(sysemu)
+    Some(flags)
 }
 
 /// Call when a context is closed to alert any tracers
diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs
index 3e7d34a1..5a04f9d5 100644
--- a/src/scheme/proc.rs
+++ b/src/scheme/proc.rs
@@ -374,7 +374,7 @@ impl Scheme for ProcScheme {
 
                 match op & PTRACE_OPERATIONMASK {
                     PTRACE_CONT => { ptrace::cont(pid); },
-                    PTRACE_SYSCALL | PTRACE_SINGLESTEP => { // <- not a bitwise OR
+                    PTRACE_SYSCALL | PTRACE_SINGLESTEP | PTRACE_SIGNAL => { // <- not a bitwise OR
                         singlestep = op & PTRACE_OPERATIONMASK == PTRACE_SINGLESTEP;
                         ptrace::set_breakpoint(pid, op);
                     },
diff --git a/src/syscall/process.rs b/src/syscall/process.rs
index 9a74c0a2..e683b4cf 100644
--- a/src/syscall/process.rs
+++ b/src/syscall/process.rs
@@ -21,7 +21,7 @@ use crate::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, PA
 use crate::ptrace;
 use crate::scheme::FileHandle;
 use crate::start::usermode;
-use crate::syscall::data::{PtraceEvent, PtraceEventContent, SigAction, Stat};
+use crate::syscall::data::{PtraceEvent, PtraceEventData, 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, PTRACE_EVENT_CLONE,
@@ -587,7 +587,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<ContextId> {
 
     let ptrace_event = PtraceEvent {
         tag: PTRACE_EVENT_CLONE,
-        data: PtraceEventContent {
+        data: PtraceEventData {
             clone: pid.into()
         }
     };
@@ -1124,9 +1124,6 @@ pub fn exit(status: usize) -> ! {
             (vfork, children)
         };
 
-        // Alert any tracers waiting for process (important: AFTER sending waitpid event)
-        ptrace::close_tracee(pid);
-
         {
             let contexts = context::contexts();
             if let Some(parent_lock) = contexts.get(ppid) {
@@ -1153,6 +1150,9 @@ pub fn exit(status: usize) -> ! {
             }
         }
 
+        // Alert any tracers waiting for process (important: AFTER sending waitpid event)
+        ptrace::close_tracee(pid);
+
         if pid == ContextId::from(1) {
             println!("Main kernel thread exited with status {:X}", status);
 
diff --git a/syscall b/syscall
index f3bb1f7b..844650c4 160000
--- a/syscall
+++ b/syscall
@@ -1 +1 @@
-Subproject commit f3bb1f7b68bc8e5544857781de9eb8729b2843f4
+Subproject commit 844650c4fb9725cd9029de6277826bfe0fb19909
-- 
GitLab