diff options
author | Andreas Kling <kling@serenityos.org> | 2021-02-08 19:34:41 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-02-08 19:34:41 +0100 |
commit | f39c2b653e32ee8f05a4cfc285315cf980224054 (patch) | |
tree | 5302b12ddfe7ddee7f9a4d95932a32d421fde566 /Kernel | |
parent | 45231051e62b82b3214c4bc2d0b3674cdd083d10 (diff) | |
download | serenity-f39c2b653e32ee8f05a4cfc285315cf980224054.zip |
Kernel: Reorganize ptrace implementation a bit
The generic parts of ptrace now live in Kernel/Syscalls/ptrace.cpp
and the i386 specific parts are moved to Arch/i386/CPU.cpp
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/Arch/i386/CPU.cpp | 34 | ||||
-rw-r--r-- | Kernel/Arch/i386/CPU.h | 6 | ||||
-rw-r--r-- | Kernel/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Kernel/CoreDump.cpp | 3 | ||||
-rw-r--r-- | Kernel/Ptrace.cpp | 194 | ||||
-rw-r--r-- | Kernel/Ptrace.h | 41 | ||||
-rw-r--r-- | Kernel/Syscalls/ptrace.cpp | 131 | ||||
-rw-r--r-- | Kernel/ThreadTracer.cpp | 5 |
8 files changed, 169 insertions, 246 deletions
diff --git a/Kernel/Arch/i386/CPU.cpp b/Kernel/Arch/i386/CPU.cpp index 798e946f22..59d80c2d2b 100644 --- a/Kernel/Arch/i386/CPU.cpp +++ b/Kernel/Arch/i386/CPU.cpp @@ -2264,6 +2264,40 @@ void Processor::set_thread_specific(u8* data, size_t len) descriptor.set_limit(len); } +void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_regs, const RegisterState& kernel_regs) +{ + ptrace_regs.eax = kernel_regs.eax, + ptrace_regs.ecx = kernel_regs.ecx, + ptrace_regs.edx = kernel_regs.edx, + ptrace_regs.ebx = kernel_regs.ebx, + ptrace_regs.esp = kernel_regs.userspace_esp, + ptrace_regs.ebp = kernel_regs.ebp, + ptrace_regs.esi = kernel_regs.esi, + ptrace_regs.edi = kernel_regs.edi, + ptrace_regs.eip = kernel_regs.eip, + ptrace_regs.eflags = kernel_regs.eflags, + ptrace_regs.cs = 0; + ptrace_regs.ss = 0; + ptrace_regs.ds = 0; + ptrace_regs.es = 0; + ptrace_regs.fs = 0; + ptrace_regs.gs = 0; +} + +void copy_ptrace_registers_into_kernel_registers(RegisterState& kernel_regs, const PtraceRegisters& ptrace_regs) +{ + kernel_regs.eax = ptrace_regs.eax; + kernel_regs.ecx = ptrace_regs.ecx; + kernel_regs.edx = ptrace_regs.edx; + kernel_regs.ebx = ptrace_regs.ebx; + kernel_regs.esp = ptrace_regs.esp; + kernel_regs.ebp = ptrace_regs.ebp; + kernel_regs.esi = ptrace_regs.esi; + kernel_regs.edi = ptrace_regs.edi; + kernel_regs.eip = ptrace_regs.eip; + kernel_regs.eflags = (kernel_regs.eflags & ~safe_eflags_mask) | (ptrace_regs.eflags & safe_eflags_mask); +} + } #ifdef DEBUG diff --git a/Kernel/Arch/i386/CPU.h b/Kernel/Arch/i386/CPU.h index 325f637bed..ae3e79a32b 100644 --- a/Kernel/Arch/i386/CPU.h +++ b/Kernel/Arch/i386/CPU.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,6 +32,7 @@ #include <AK/Vector.h> #include <Kernel/PhysicalAddress.h> #include <Kernel/VirtualAddress.h> +#include <LibC/sys/arch/i386/regs.h> #define PAGE_SIZE 4096 #define GENERIC_INTERRUPT_HANDLERS_COUNT (256 - IRQ_VECTOR_BASE) @@ -494,6 +495,9 @@ struct [[gnu::packed]] RegisterState { #define REGISTER_STATE_SIZE (19 * 4) static_assert(REGISTER_STATE_SIZE == sizeof(RegisterState)); +void copy_kernel_registers_into_ptrace_registers(PtraceRegisters&, const RegisterState&); +void copy_ptrace_registers_into_kernel_registers(RegisterState&, const PtraceRegisters&); + struct [[gnu::aligned(16)]] FPUState { u8 buffer[512]; diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index a5e8762685..ddee7aaa67 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -104,7 +104,6 @@ set(KERNEL_SOURCES PerformanceEventBuffer.cpp Process.cpp ProcessGroup.cpp - Ptrace.cpp RTC.cpp Random.cpp Scheduler.cpp diff --git a/Kernel/CoreDump.cpp b/Kernel/CoreDump.cpp index 4c947f197f..c1fd3f55d1 100644 --- a/Kernel/CoreDump.cpp +++ b/Kernel/CoreDump.cpp @@ -34,7 +34,6 @@ #include <Kernel/FileSystem/FileDescription.h> #include <Kernel/FileSystem/VirtualFileSystem.h> #include <Kernel/Process.h> -#include <Kernel/Ptrace.h> #include <Kernel/RTC.h> #include <Kernel/SpinLock.h> #include <Kernel/VM/ProcessPagingScope.h> @@ -246,7 +245,7 @@ ByteBuffer CoreDump::create_notes_threads_data() const ELF::Core::ThreadInfo info {}; info.header.type = ELF::Core::NotesEntryHeader::Type::ThreadInfo; info.tid = thread.tid().value(); - Ptrace::copy_kernel_registers_into_ptrace_registers(info.regs, thread.get_register_dump_from_stack()); + copy_kernel_registers_into_ptrace_registers(info.regs, thread.get_register_dump_from_stack()); entry_buff.append((void*)&info, sizeof(info)); diff --git a/Kernel/Ptrace.cpp b/Kernel/Ptrace.cpp deleted file mode 100644 index e39b3de21b..0000000000 --- a/Kernel/Ptrace.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <Kernel/Process.h> -#include <Kernel/Ptrace.h> -#include <Kernel/Thread.h> -#include <Kernel/ThreadTracer.h> -#include <Kernel/VM/MemoryManager.h> -#include <Kernel/VM/ProcessPagingScope.h> - -namespace Ptrace { - -KResultOr<u32> handle_syscall(const Kernel::Syscall::SC_ptrace_params& params, Process& caller) -{ - ScopedSpinLock scheduler_lock(g_scheduler_lock); - if (params.request == PT_TRACE_ME) { - if (Process::current()->tracer()) - return EBUSY; - - caller.set_wait_for_tracer_at_next_execve(true); - return KSuccess; - } - - // FIXME: PID/TID BUG - // This bug allows to request PT_ATTACH (or anything else) the same process, as - // long it is not the main thread. Alternatively, if this is desired, then the - // bug is that this prevents PT_ATTACH to the main thread from another thread. - if (params.tid == caller.pid().value()) - return EINVAL; - - auto peer = Thread::from_tid(params.tid); - if (!peer) - return ESRCH; - - if ((peer->process().uid() != caller.euid()) - || (peer->process().uid() != peer->process().euid())) // Disallow tracing setuid processes - return EACCES; - - if (!peer->process().is_dumpable()) - return EACCES; - - auto& peer_process = peer->process(); - if (params.request == PT_ATTACH) { - if (peer_process.tracer()) { - return EBUSY; - } - peer_process.start_tracing_from(caller.pid()); - ScopedSpinLock lock(peer->get_lock()); - if (peer->state() != Thread::State::Stopped) { - peer->send_signal(SIGSTOP, &caller); - } - return KSuccess; - } - - auto* tracer = peer_process.tracer(); - - if (!tracer) - return EPERM; - - if (tracer->tracer_pid() != caller.pid()) - return EBUSY; - - if (peer->state() == Thread::State::Running) - return EBUSY; - - scheduler_lock.unlock(); - - switch (params.request) { - case PT_CONTINUE: - peer->send_signal(SIGCONT, &caller); - break; - - case PT_DETACH: - peer_process.stop_tracing(); - peer->send_signal(SIGCONT, &caller); - break; - - case PT_SYSCALL: - tracer->set_trace_syscalls(true); - peer->send_signal(SIGCONT, &caller); - break; - - case PT_GETREGS: { - if (!tracer->has_regs()) - return EINVAL; - auto* regs = reinterpret_cast<PtraceRegisters*>(params.addr); - if (!copy_to_user(regs, &tracer->regs())) - return EFAULT; - break; - } - - case PT_SETREGS: { - if (!tracer->has_regs()) - return EINVAL; - - PtraceRegisters regs; - if (!copy_from_user(®s, (const PtraceRegisters*)params.addr)) - return EFAULT; - - 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) - return EFAULT; - - tracer->set_regs(regs); - copy_ptrace_registers_into_kernel_registers(peer_saved_registers, regs); - break; - } - - case PT_PEEK: { - Kernel::Syscall::SC_ptrace_peek_params peek_params; - if (!copy_from_user(&peek_params, reinterpret_cast<Kernel::Syscall::SC_ptrace_peek_params*>(params.addr))) - return EFAULT; - if (!is_user_address(VirtualAddress { peek_params.address })) - return EFAULT; - auto result = peer->process().peek_user_data(Userspace<const u32*> { (FlatPtr)peek_params.address }); - if (result.is_error()) - return result.error(); - if (!copy_to_user(peek_params.out_data, &result.value())) - return EFAULT; - break; - } - - case PT_POKE: - if (!is_user_address(VirtualAddress { params.addr })) - return EFAULT; - return peer->process().poke_user_data(Userspace<u32*> { (FlatPtr)params.addr }, params.data); - - default: - return EINVAL; - } - - return KSuccess; -} - -void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_regs, const RegisterState& kernel_regs) -{ - ptrace_regs.eax = kernel_regs.eax, - ptrace_regs.ecx = kernel_regs.ecx, - ptrace_regs.edx = kernel_regs.edx, - ptrace_regs.ebx = kernel_regs.ebx, - ptrace_regs.esp = kernel_regs.userspace_esp, - ptrace_regs.ebp = kernel_regs.ebp, - ptrace_regs.esi = kernel_regs.esi, - ptrace_regs.edi = kernel_regs.edi, - ptrace_regs.eip = kernel_regs.eip, - ptrace_regs.eflags = kernel_regs.eflags, - ptrace_regs.cs = 0; - ptrace_regs.ss = 0; - ptrace_regs.ds = 0; - ptrace_regs.es = 0; - ptrace_regs.fs = 0; - ptrace_regs.gs = 0; -} - -void copy_ptrace_registers_into_kernel_registers(RegisterState& kernel_regs, const PtraceRegisters& ptrace_regs) -{ - kernel_regs.eax = ptrace_regs.eax; - kernel_regs.ecx = ptrace_regs.ecx; - kernel_regs.edx = ptrace_regs.edx; - kernel_regs.ebx = ptrace_regs.ebx; - kernel_regs.esp = ptrace_regs.esp; - kernel_regs.ebp = ptrace_regs.ebp; - kernel_regs.esi = ptrace_regs.esi; - kernel_regs.edi = ptrace_regs.edi; - kernel_regs.eip = ptrace_regs.eip; - - kernel_regs.eflags = (kernel_regs.eflags & ~safe_eflags_mask) | (ptrace_regs.eflags & safe_eflags_mask); -} - -} diff --git a/Kernel/Ptrace.h b/Kernel/Ptrace.h deleted file mode 100644 index 38d86b6830..0000000000 --- a/Kernel/Ptrace.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <Kernel/API/Syscall.h> -#include <Kernel/KResult.h> -#include <Kernel/Process.h> -#include <LibC/sys/arch/i386/regs.h> - -namespace Ptrace { - -KResultOr<u32> handle_syscall(const Kernel::Syscall::SC_ptrace_params& params, Process& caller); - -void copy_kernel_registers_into_ptrace_registers(PtraceRegisters&, const RegisterState&); -void copy_ptrace_registers_into_kernel_registers(RegisterState&, const PtraceRegisters&); - -} diff --git a/Kernel/Syscalls/ptrace.cpp b/Kernel/Syscalls/ptrace.cpp index 22cb45ec49..b385f7d7ab 100644 --- a/Kernel/Syscalls/ptrace.cpp +++ b/Kernel/Syscalls/ptrace.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com> + * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,7 +27,6 @@ #include <AK/ScopeGuard.h> #include <Kernel/Process.h> -#include <Kernel/Ptrace.h> #include <Kernel/VM/MemoryManager.h> #include <Kernel/VM/PrivateInodeVMObject.h> #include <Kernel/VM/ProcessPagingScope.h> @@ -35,13 +35,136 @@ namespace Kernel { +static KResultOr<u32> handle_ptrace(const Kernel::Syscall::SC_ptrace_params& params, Process& caller) +{ + ScopedSpinLock scheduler_lock(g_scheduler_lock); + if (params.request == PT_TRACE_ME) { + if (Process::current()->tracer()) + return EBUSY; + + caller.set_wait_for_tracer_at_next_execve(true); + return KSuccess; + } + + // FIXME: PID/TID BUG + // This bug allows to request PT_ATTACH (or anything else) the same process, as + // long it is not the main thread. Alternatively, if this is desired, then the + // bug is that this prevents PT_ATTACH to the main thread from another thread. + if (params.tid == caller.pid().value()) + return EINVAL; + + auto peer = Thread::from_tid(params.tid); + if (!peer) + return ESRCH; + + if ((peer->process().uid() != caller.euid()) + || (peer->process().uid() != peer->process().euid())) // Disallow tracing setuid processes + return EACCES; + + if (!peer->process().is_dumpable()) + return EACCES; + + auto& peer_process = peer->process(); + if (params.request == PT_ATTACH) { + if (peer_process.tracer()) { + return EBUSY; + } + peer_process.start_tracing_from(caller.pid()); + ScopedSpinLock lock(peer->get_lock()); + if (peer->state() != Thread::State::Stopped) { + peer->send_signal(SIGSTOP, &caller); + } + return KSuccess; + } + + auto* tracer = peer_process.tracer(); + + if (!tracer) + return EPERM; + + if (tracer->tracer_pid() != caller.pid()) + return EBUSY; + + if (peer->state() == Thread::State::Running) + return EBUSY; + + scheduler_lock.unlock(); + + switch (params.request) { + case PT_CONTINUE: + peer->send_signal(SIGCONT, &caller); + break; + + case PT_DETACH: + peer_process.stop_tracing(); + peer->send_signal(SIGCONT, &caller); + break; + + case PT_SYSCALL: + tracer->set_trace_syscalls(true); + peer->send_signal(SIGCONT, &caller); + break; + + case PT_GETREGS: { + if (!tracer->has_regs()) + return EINVAL; + auto* regs = reinterpret_cast<PtraceRegisters*>(params.addr); + if (!copy_to_user(regs, &tracer->regs())) + return EFAULT; + break; + } + + case PT_SETREGS: { + if (!tracer->has_regs()) + return EINVAL; + + PtraceRegisters regs {}; + if (!copy_from_user(®s, (const PtraceRegisters*)params.addr)) + return EFAULT; + + 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) + return EFAULT; + + tracer->set_regs(regs); + copy_ptrace_registers_into_kernel_registers(peer_saved_registers, regs); + break; + } + + case PT_PEEK: { + Kernel::Syscall::SC_ptrace_peek_params peek_params {}; + if (!copy_from_user(&peek_params, reinterpret_cast<Kernel::Syscall::SC_ptrace_peek_params*>(params.addr))) + return EFAULT; + if (!is_user_address(VirtualAddress { peek_params.address })) + return EFAULT; + auto result = peer->process().peek_user_data(Userspace<const u32*> { (FlatPtr)peek_params.address }); + if (result.is_error()) + return result.error(); + if (!copy_to_user(peek_params.out_data, &result.value())) + return EFAULT; + break; + } + + case PT_POKE: + if (!is_user_address(VirtualAddress { params.addr })) + return EFAULT; + return peer->process().poke_user_data(Userspace<u32*> { (FlatPtr)params.addr }, params.data); + + default: + return EINVAL; + } + + return KSuccess; +} + int Process::sys$ptrace(Userspace<const Syscall::SC_ptrace_params*> user_params) { REQUIRE_PROMISE(ptrace); - Syscall::SC_ptrace_params params; + Syscall::SC_ptrace_params params {}; if (!copy_from_user(¶ms, user_params)) return -EFAULT; - auto result = Ptrace::handle_syscall(params, *this); + auto result = handle_ptrace(params, *this); return result.is_error() ? result.error().error() : result.value(); } diff --git a/Kernel/ThreadTracer.cpp b/Kernel/ThreadTracer.cpp index ded4a812f4..2f426aba1f 100644 --- a/Kernel/ThreadTracer.cpp +++ b/Kernel/ThreadTracer.cpp @@ -27,7 +27,6 @@ #include <AK/Memory.h> #include <AK/kmalloc.h> #include <Kernel/Arch/i386/CPU.h> -#include <Kernel/Ptrace.h> #include <Kernel/ThreadTracer.h> namespace Kernel { @@ -39,8 +38,8 @@ ThreadTracer::ThreadTracer(ProcessID tracer_pid) void ThreadTracer::set_regs(const RegisterState& regs) { - PtraceRegisters r; - Ptrace::copy_kernel_registers_into_ptrace_registers(r, regs); + PtraceRegisters r {}; + copy_kernel_registers_into_ptrace_registers(r, regs); m_regs = r; } |