diff --git a/Kernel/Ptrace.cpp b/Kernel/Ptrace.cpp index bf8c191931..3540572435 100644 --- a/Kernel/Ptrace.cpp +++ b/Kernel/Ptrace.cpp @@ -92,8 +92,10 @@ KResultOr handle_syscall(const Kernel::Syscall::SC_ptrace_params& params, P auto& peer_saved_registers = peer->get_register_dump_from_stack(); // Verify that the saved registers are in usermode context - if ((peer_saved_registers.cs & 0x03) != 3) + if ((peer_saved_registers.cs & 0x03) != 3) { return -EFAULT; + } + { SmapDisabler disabler; PtraceRegisters* regs = reinterpret_cast(params.addr); @@ -104,12 +106,24 @@ KResultOr handle_syscall(const Kernel::Syscall::SC_ptrace_params& params, P } case PT_PEEK: { - u32* addr = reinterpret_cast(params.addr); - return peer->process().peek_user_data(addr); + Kernel::Syscall::SC_ptrace_peek_params peek_params; + if (!caller.validate_read_and_copy_typed(&peek_params, reinterpret_cast(params.addr))) + return -EFAULT; + // read validation is done inside 'peek_user_data' + auto result = peer->process().peek_user_data(peek_params.address); + if (result.is_error()) + return -EFAULT; + peer->process().validate_write(peek_params.out_data, sizeof(u32)); + { + SmapDisabler disabler; + *(peek_params.out_data) = result.value(); + } + break; } case PT_POKE: { u32* addr = reinterpret_cast(params.addr); + // write validation is done inside 'poke_user_data' return peer->process().poke_user_data(addr, params.data); } diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 3c2c876e8a..c0820c45a9 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -432,6 +432,11 @@ struct SC_ptrace_params { int data; }; +struct SC_ptrace_peek_params { + u32* address; + u32* out_data; +}; + void initialize(); int sync(); diff --git a/Libraries/LibC/sys/ptrace.cpp b/Libraries/LibC/sys/ptrace.cpp index b9c0c16234..f991b077bc 100644 --- a/Libraries/LibC/sys/ptrace.cpp +++ b/Libraries/LibC/sys/ptrace.cpp @@ -24,6 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -32,6 +33,20 @@ extern "C" { int ptrace(int request, pid_t pid, void* addr, int data) { + + // PT_PEEK needs special handling since the syscall wrapper + // returns the peeked value as an int, which can be negative because of the cast. + // When using PT_PEEK, the user can check if an error occured + // by looking at errno rather than the return value. + + u32 out_data; + Syscall::SC_ptrace_peek_params peek_params; + if (request == PT_PEEK) { + peek_params.address = reinterpret_cast(addr); + peek_params.out_data = &out_data; + addr = &peek_params; + } + Syscall::SC_ptrace_params params { request, pid, @@ -39,6 +54,16 @@ int ptrace(int request, pid_t pid, void* addr, int data) data }; int rc = syscall(SC_ptrace, ¶ms); + + if (request == PT_PEEK) { + if (rc < 0) { + errno = -rc; + return -1; + } + errno = 0; + return static_cast(out_data); + } + __RETURN_WITH_ERRNO(rc, rc, -1); } }