kernel: improve comments in sys/ptrace.c
This commit is contained in:
parent
74e1d8c62c
commit
b297ab3fed
@ -5,10 +5,23 @@
|
|||||||
* Provides single stepping, cross-process memory inspection,
|
* Provides single stepping, cross-process memory inspection,
|
||||||
* regiser inspection, poking, and syscall trace events.
|
* regiser inspection, poking, and syscall trace events.
|
||||||
*
|
*
|
||||||
|
* @warning This ptrace implementation is incomplete.
|
||||||
|
*
|
||||||
|
* We are missing a lot of @c ptrace functionality found in other
|
||||||
|
* operating systems, and even some of the functionality we have is
|
||||||
|
* only partially implemented or may not work as it should.
|
||||||
|
*
|
||||||
|
* This implementation was intended primarily to support having a
|
||||||
|
* @c strace command in userspace, and also provides some limited
|
||||||
|
* support for a debugger.
|
||||||
|
*
|
||||||
|
* @see apps/dbg.c
|
||||||
|
* @see apps/strace.c
|
||||||
|
*
|
||||||
* @copyright
|
* @copyright
|
||||||
* This file is part of ToaruOS and is released under the terms
|
* This file is part of ToaruOS and is released under the terms
|
||||||
* of the NCSA / University of Illinois License - see LICENSE.md
|
* of the NCSA / University of Illinois License - see LICENSE.md
|
||||||
* Copyright (C) 2021 K. Lange
|
* Copyright (C) 2021-2022 K. Lange
|
||||||
*/
|
*/
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@ -30,6 +43,18 @@
|
|||||||
#error "no regs"
|
#error "no regs"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Internally set the tracer of a tracee process.
|
||||||
|
*
|
||||||
|
* Sets up @p tracer to trace @p tracee and sets @p tracee as
|
||||||
|
* tracing the default events (syscalls and signals).
|
||||||
|
*
|
||||||
|
* A tracer can trace multiple tracees, but a tracee can only be
|
||||||
|
* traced by one tracer.
|
||||||
|
*
|
||||||
|
* @param tracer Process that is the doing the tracing
|
||||||
|
* @param tracee Process that is breing traced
|
||||||
|
*/
|
||||||
static void _ptrace_trace(process_t * tracer, process_t * tracee) {
|
static void _ptrace_trace(process_t * tracer, process_t * tracee) {
|
||||||
spin_lock(tracer->wait_lock);
|
spin_lock(tracer->wait_lock);
|
||||||
__sync_or_and_fetch(&tracee->flags, (PROC_FLAG_TRACE_SYSCALLS | PROC_FLAG_TRACE_SIGNALS));
|
__sync_or_and_fetch(&tracee->flags, (PROC_FLAG_TRACE_SYSCALLS | PROC_FLAG_TRACE_SIGNALS));
|
||||||
@ -45,6 +70,21 @@ static void _ptrace_trace(process_t * tracer, process_t * tracee) {
|
|||||||
spin_unlock(tracer->wait_lock);
|
spin_unlock(tracer->wait_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start tracing a process.
|
||||||
|
*
|
||||||
|
* @ref PTRACE_ATTACH
|
||||||
|
*
|
||||||
|
* Sets the current process to be the tracer for the target tracee.
|
||||||
|
* Both the tracer and tracee will resume normally, until the next
|
||||||
|
* ptrace event stops the tracee.
|
||||||
|
*
|
||||||
|
* TODO What happens if the process is already being traced?
|
||||||
|
*
|
||||||
|
* @param pid Tracee ID
|
||||||
|
* @returns 0 on success, -ESRCH if the tracee is invalid, -EPERM if the tracee
|
||||||
|
* is not owned by the same user as the tracer and the tracer is not root.
|
||||||
|
*/
|
||||||
long ptrace_attach(pid_t pid) {
|
long ptrace_attach(pid_t pid) {
|
||||||
process_t * tracer = (process_t *)this_core->current_process;
|
process_t * tracer = (process_t *)this_core->current_process;
|
||||||
process_t * tracee = process_from_pid(pid);
|
process_t * tracee = process_from_pid(pid);
|
||||||
@ -56,6 +96,21 @@ long ptrace_attach(pid_t pid) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the current process to be traced by its parent.
|
||||||
|
*
|
||||||
|
* @ref PTRACE_TRACEME
|
||||||
|
*
|
||||||
|
* Generally, this is used through the @c ptrace system call by
|
||||||
|
* the debugger or @c strace implementation after forking a child
|
||||||
|
* process and before calling @c exec.
|
||||||
|
*
|
||||||
|
* The calling process will resume immediately.
|
||||||
|
*
|
||||||
|
* TODO What happens if we are already being traced?
|
||||||
|
*
|
||||||
|
* @returns 0 on success, -EINVAL if the parent was not found.
|
||||||
|
*/
|
||||||
long ptrace_self(void) {
|
long ptrace_self(void) {
|
||||||
process_t * tracee = (process_t*)this_core->current_process;
|
process_t * tracee = (process_t*)this_core->current_process;
|
||||||
process_t * tracer = process_get_parent(tracee);
|
process_t * tracer = process_get_parent(tracee);
|
||||||
@ -68,6 +123,33 @@ long ptrace_self(void) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Trigger a ptrace event on the currently executing thread.
|
* @brief Trigger a ptrace event on the currently executing thread.
|
||||||
|
*
|
||||||
|
* @ref PTRACE_EVENT_SINGLESTEP
|
||||||
|
* @ref PTRACE_EVENT_SYSCALL_ENTER
|
||||||
|
* @ref PTRACE_EVENT_SYSCALL_EXIT
|
||||||
|
*
|
||||||
|
* Called elsewhere in the kernel when a trace event happens that is
|
||||||
|
* not currently being ignored, such as upon entry into a syscall handler,
|
||||||
|
* or exit from a syscall handler, or before a signal would be delivered.
|
||||||
|
*
|
||||||
|
* Runs in the kernel context of the tracee, causes the tracee to be suspended
|
||||||
|
* and awakens the tracer to return from its @c ptrace call.
|
||||||
|
*
|
||||||
|
* When the kernel context for this process is resumed, the signal number
|
||||||
|
* will be checked from the tracee's status and returned to caller that
|
||||||
|
* initiated the ptrace event.
|
||||||
|
*
|
||||||
|
* When resuming from a signal event, the new signal number will replace the
|
||||||
|
* old signal number. In this case, if the new signal number is 0 it will
|
||||||
|
* be discarded and the tracee will continue as if it had ignored it.
|
||||||
|
*
|
||||||
|
* When resuming from other events, signals are generally sent directly
|
||||||
|
* and the process will act on the signal when it has an opportunity to
|
||||||
|
* return to userspace.
|
||||||
|
*
|
||||||
|
* @param signal Signal number if @p reason is 0.
|
||||||
|
* @param reason PTRACE_EVENT value describing the event; 0 for signal delivery.
|
||||||
|
* @returns Signal number from tracee status upon resumption.
|
||||||
*/
|
*/
|
||||||
long ptrace_signal(int signal, int reason) {
|
long ptrace_signal(int signal, int reason) {
|
||||||
this_core->current_process->status = 0x7F | (signal << 8) | (reason << 16);
|
this_core->current_process->status = 0x7F | (signal << 8) | (reason << 16);
|
||||||
@ -86,6 +168,17 @@ long ptrace_signal(int signal, int reason) {
|
|||||||
return signum;
|
return signum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resume a traced process.
|
||||||
|
*
|
||||||
|
* Unsuspends the traced process, sending an appropriate signal if one
|
||||||
|
* was currently pending or if one was sent by the tracer through either
|
||||||
|
* of @ref ptrace_continue or @ref ptrace_detach.
|
||||||
|
*
|
||||||
|
* @param pid Tracee ID
|
||||||
|
* @param tracee Tracee process object
|
||||||
|
* @param sig Signal number to send, or 0 if none.
|
||||||
|
*/
|
||||||
static void signal_and_continue(pid_t pid, process_t * tracee, int sig) {
|
static void signal_and_continue(pid_t pid, process_t * tracee, int sig) {
|
||||||
/* Unsuspend */
|
/* Unsuspend */
|
||||||
__sync_and_and_fetch(&tracee->flags, ~(PROC_FLAG_SUSPENDED));
|
__sync_and_and_fetch(&tracee->flags, ~(PROC_FLAG_SUSPENDED));
|
||||||
@ -101,6 +194,20 @@ static void signal_and_continue(pid_t pid, process_t * tracee, int sig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resume the tracee until the next event.
|
||||||
|
*
|
||||||
|
* @ref PTRACE_CONT
|
||||||
|
*
|
||||||
|
* Allows the tracee to resume execution, while optionally sending
|
||||||
|
* a signal. This signal may be the one that triggered the ptrace
|
||||||
|
* event from which the process is being resumed, or a new signal,
|
||||||
|
* or no signal at all.
|
||||||
|
*
|
||||||
|
* @param pid Tracee ID
|
||||||
|
* @param sig Signal to send to tracee on resume, or 0 for none.
|
||||||
|
* @returns 0 on success, -ESRCH if tracee is invalid.
|
||||||
|
*/
|
||||||
long ptrace_continue(pid_t pid, int sig) {
|
long ptrace_continue(pid_t pid, int sig) {
|
||||||
process_t * tracee = process_from_pid(pid);
|
process_t * tracee = process_from_pid(pid);
|
||||||
if (!tracee || (tracee->tracer != this_core->current_process->id) || !(tracee->flags & PROC_FLAG_SUSPENDED)) return -ESRCH;
|
if (!tracee || (tracee->tracer != this_core->current_process->id) || !(tracee->flags & PROC_FLAG_SUSPENDED)) return -ESRCH;
|
||||||
@ -110,6 +217,17 @@ long ptrace_continue(pid_t pid, int sig) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stop tracing a tracee.
|
||||||
|
*
|
||||||
|
* @ref PTRACE_DETACH
|
||||||
|
*
|
||||||
|
* Marks the tracee as no longer being traced and resumes it.
|
||||||
|
*
|
||||||
|
* @param pid Tracee ID
|
||||||
|
* @param sig Signal to send to tracee on resume, or 0 for none.
|
||||||
|
* @returns 0 on success, -ESRCH if tracee is invalid.
|
||||||
|
*/
|
||||||
long ptrace_detach(pid_t pid, int sig) {
|
long ptrace_detach(pid_t pid, int sig) {
|
||||||
process_t * tracee = process_from_pid(pid);
|
process_t * tracee = process_from_pid(pid);
|
||||||
if (!tracee || (tracee->tracer != this_core->current_process->id) || !(tracee->flags & PROC_FLAG_SUSPENDED)) return -ESRCH;
|
if (!tracee || (tracee->tracer != this_core->current_process->id) || !(tracee->flags & PROC_FLAG_SUSPENDED)) return -ESRCH;
|
||||||
@ -122,6 +240,28 @@ long ptrace_detach(pid_t pid, int sig) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Obtain the register context of the tracee.
|
||||||
|
*
|
||||||
|
* @ref PTRACE_GETREGS
|
||||||
|
*
|
||||||
|
* Copies the interrupt register context of the tracee into a tracer-provided
|
||||||
|
* address. The size, meaning, and layout of the data copied is architecture-dependent.
|
||||||
|
*
|
||||||
|
* Currently this is either @c interrupt_registers or @c syscall_registers, depending
|
||||||
|
* on what is available. Since the tracee needs to be suspended this should represent
|
||||||
|
* the actual userspace register context when it resumes.
|
||||||
|
*
|
||||||
|
* On AArch64 we also add ELR, which isn't in the interrupt or syscall register contexts,
|
||||||
|
* but pushed somewhere else...
|
||||||
|
*
|
||||||
|
* TODO We should support reading FPU regs as well.
|
||||||
|
* TODO @c PTRACE_SETREGS so we can modify them.
|
||||||
|
*
|
||||||
|
* @param pid Tracee ID
|
||||||
|
* @param data Address in tracer to write data into.
|
||||||
|
* @returns 0 on success, -ESRCH if tracee is invalid.
|
||||||
|
*/
|
||||||
long ptrace_getregs(pid_t pid, void * data) {
|
long ptrace_getregs(pid_t pid, void * data) {
|
||||||
if (!data || ptr_validate(data, "ptrace")) return -EFAULT;
|
if (!data || ptr_validate(data, "ptrace")) return -EFAULT;
|
||||||
process_t * tracee = process_from_pid(pid);
|
process_t * tracee = process_from_pid(pid);
|
||||||
@ -136,6 +276,21 @@ long ptrace_getregs(pid_t pid, void * data) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read one byte from the tracee's memory.
|
||||||
|
*
|
||||||
|
* @ref PTRACE_PEEKDATA
|
||||||
|
*
|
||||||
|
* Reads one byte of data from the tracee process's memory space.
|
||||||
|
* Other implementations of @c PTRACE_PEEKDATA may write other sizes of data,
|
||||||
|
* but to make this as straightforward as possible, we only support single
|
||||||
|
* bytes. Maybe in the future we'll support other sizes...
|
||||||
|
*
|
||||||
|
* @param pid Tracee ID
|
||||||
|
* @param addr Virtual address in the tracee context to write to.
|
||||||
|
* @param data Address in the tracer to store the read byte into.
|
||||||
|
* @returns 0 on success, -EFAULT if the requested address is not mapped and readable in the tracee, -ESRCH if tracee is invalid.
|
||||||
|
*/
|
||||||
long ptrace_peek(pid_t pid, void * addr, void * data) {
|
long ptrace_peek(pid_t pid, void * addr, void * data) {
|
||||||
if (!data || ptr_validate(data, "ptrace")) return -EFAULT;
|
if (!data || ptr_validate(data, "ptrace")) return -EFAULT;
|
||||||
process_t * tracee = process_from_pid(pid);
|
process_t * tracee = process_from_pid(pid);
|
||||||
@ -158,6 +313,24 @@ long ptrace_peek(pid_t pid, void * addr, void * data) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Place a byte of data into the tracee's memory.
|
||||||
|
*
|
||||||
|
* @ref PTRACE_POKEDATA
|
||||||
|
*
|
||||||
|
* Writes one byte of data into the tracee process's memory space.
|
||||||
|
* Other implementations of @c PTRACE_POKEDATA may write other sizes of data,
|
||||||
|
* but to make this as straightforward as possible, we only support single
|
||||||
|
* bytes. Maybe in the future we'll support other sizes...
|
||||||
|
*
|
||||||
|
* TODO This uses mmu_map_from_physical and doesn't do any cache maintenance?
|
||||||
|
* It will probably break when, eg., poking instructions on ARM...
|
||||||
|
*
|
||||||
|
* @param pid Tracee ID
|
||||||
|
* @param addr Virtual address in the tracee context to write to.
|
||||||
|
* @param data Address in the tracer context to read one byte from.
|
||||||
|
* @returns 0 on success, -ESRCH if tracee is invalid, -EFAULT if the tracee address is not mapped or not writable.
|
||||||
|
*/
|
||||||
long ptrace_poke(pid_t pid, void * addr, void * data) {
|
long ptrace_poke(pid_t pid, void * addr, void * data) {
|
||||||
if (!data || ptr_validate(data, "ptrace")) return -EFAULT;
|
if (!data || ptr_validate(data, "ptrace")) return -EFAULT;
|
||||||
process_t * tracee = process_from_pid(pid);
|
process_t * tracee = process_from_pid(pid);
|
||||||
@ -180,6 +353,21 @@ long ptrace_poke(pid_t pid, void * addr, void * data) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disable tracing of syscalls in the tracee.
|
||||||
|
*
|
||||||
|
* @ref PTRACE_SIGNALS_ONLY_PLZ
|
||||||
|
*
|
||||||
|
* Turns off tracing of syscalls in the tracee. Only signals will be
|
||||||
|
* traced. To turn syscall tracing back on, restart tracing by detaching
|
||||||
|
* and re-attaching to the tracee.
|
||||||
|
*
|
||||||
|
* TODO We need a better interface to configure tracing, so we can offer
|
||||||
|
* more complex options than just signals and syscalls...
|
||||||
|
*
|
||||||
|
* @param pid Tracee ID
|
||||||
|
* @returns 0 on success, -ESRCH if the tracee was not found or the current process is not its tracer.
|
||||||
|
*/
|
||||||
long ptrace_signals_only(pid_t pid) {
|
long ptrace_signals_only(pid_t pid) {
|
||||||
process_t * tracee = process_from_pid(pid);
|
process_t * tracee = process_from_pid(pid);
|
||||||
if (!tracee || (tracee->tracer != this_core->current_process->id) || !(tracee->flags & PROC_FLAG_SUSPENDED)) return -ESRCH;
|
if (!tracee || (tracee->tracer != this_core->current_process->id) || !(tracee->flags & PROC_FLAG_SUSPENDED)) return -ESRCH;
|
||||||
@ -187,6 +375,23 @@ long ptrace_signals_only(pid_t pid) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable single-stepping for a process.
|
||||||
|
*
|
||||||
|
* @ref PTRACE_SINGLESTEP
|
||||||
|
*
|
||||||
|
* Enables an architecture-specific mechanism for single step debugging
|
||||||
|
* in the requested process. When the process resumes, it will execute
|
||||||
|
* one instruction and then fault back to the kernel, and the tracer
|
||||||
|
* will be alerted.
|
||||||
|
*
|
||||||
|
* Single stepping will be disabled again when the process returns from
|
||||||
|
* the fault, and must be re-enabled by another call to @c ptrace_singlstep.
|
||||||
|
*
|
||||||
|
* @param pid ID of the process to enable single-step for
|
||||||
|
* @param sig Signal number to hand to the process when it resumes, or 0.
|
||||||
|
* @returns 0 on success, -ESRCH if the process could not be found or is not a tracee of the current process.
|
||||||
|
*/
|
||||||
long ptrace_singlestep(pid_t pid, int sig) {
|
long ptrace_singlestep(pid_t pid, int sig) {
|
||||||
process_t * tracee = process_from_pid(pid);
|
process_t * tracee = process_from_pid(pid);
|
||||||
if (!tracee || (tracee->tracer != this_core->current_process->id) || !(tracee->flags & PROC_FLAG_SUSPENDED)) return -ESRCH;
|
if (!tracee || (tracee->tracer != this_core->current_process->id) || !(tracee->flags & PROC_FLAG_SUSPENDED)) return -ESRCH;
|
||||||
@ -206,6 +411,21 @@ long ptrace_singlestep(pid_t pid, int sig) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle ptrace system call requests.
|
||||||
|
*
|
||||||
|
* Internal interface for dispatching @c ptrace system calls. Maps
|
||||||
|
* arguments from the system call to the various ptrace functions.
|
||||||
|
*
|
||||||
|
* @note This is the direct system call implementation. Data coming
|
||||||
|
* in here is directly from the arguments of the system call.
|
||||||
|
*
|
||||||
|
* @param request Request type
|
||||||
|
* @param pid Tracee ID
|
||||||
|
* @param addr Address to peek or poke
|
||||||
|
* @param data Place to put or read data, depending on the function
|
||||||
|
* @returns Generally, status codes. -EINVAL for an invalid request.
|
||||||
|
*/
|
||||||
long ptrace_handle(long request, pid_t pid, void * addr, void * data) {
|
long ptrace_handle(long request, pid_t pid, void * addr, void * data) {
|
||||||
switch (request) {
|
switch (request) {
|
||||||
case PTRACE_ATTACH:
|
case PTRACE_ATTACH:
|
||||||
|
Loading…
Reference in New Issue
Block a user