Repository: SerenityOS/serenity
File: Kernel/Syscalls/ptrace.cpp (C++)

Code before:
#include <Kernel/Arch/aarch64/Registers.h>
#include <Kernel/Memory/PrivateInodeVMObject.h>
#include <Kernel/Memory/Region.h>
#include <Kernel/Memory/ScopedAddressSpaceSwitcher.h>
#include <Kernel/Memory/SharedInodeVMObject.h>
#include <Kernel/Tasks/Process.h>
#include <Kernel/Tasks/Scheduler.h>
#include <Kernel/Tasks/ThreadTracer.h>

namespace Kernel {

static ErrorOr<FlatPtr> handle_ptrace(Kernel::Syscall::SC_ptrace_params const& params, Process& caller)
{
    if (params.request == PT_TRACE_ME) {
        if (Process::current().tracer())
            return EBUSY;

        caller.set_wait_for_tracer_at_next_execve(true);
        return 0;
    }

    auto peer = Thread::from_tid_in_same_process_list(params.tid);
    if (!peer)
        return ESRCH;

    if (params.request == PT_ATTACH && peer->pid() == caller.pid().value())
        return EPERM;

    MutexLocker ptrace_locker(peer->process().ptrace_lock());
    SpinlockLocker scheduler_lock(g_scheduler_lock);

    auto peer_credentials = peer->process().credentials();
    auto caller_credentials = caller.credentials();
    if (!caller_credentials->is_superuser() && ((peer_credentials->uid() != caller_credentials->euid()) || (peer_credentials->uid() != peer_credentials->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;
        }
        TRY(peer_process.start_tracing_from(caller.pid()));
        SpinlockLocker lock(peer->get_lock());
        if (peer->state() == Thread::State::Stopped) {
            peer_process.tracer()->set_regs(peer->get_register_dump_from_stack());
        } else {
            peer->send_signal(SIGSTOP, &caller);
        }

Review comment (bug):
Why do you explicitly allow this for requests other than PT_ATTACH?

Shouldn't other requests fail regardless if the PT_ATTACH didn't succeed?

Code after:
#include <Kernel/Arch/aarch64/Registers.h>
#include <Kernel/Memory/PrivateInodeVMObject.h>
#include <Kernel/Memory/Region.h>
#include <Kernel/Memory/ScopedAddressSpaceSwitcher.h>
#include <Kernel/Memory/SharedInodeVMObject.h>
#include <Kernel/Tasks/Process.h>
#include <Kernel/Tasks/Scheduler.h>
#include <Kernel/Tasks/ThreadTracer.h>

namespace Kernel {

static ErrorOr<FlatPtr> handle_ptrace(Kernel::Syscall::SC_ptrace_params const& params, Process& caller)
{
    if (params.request == PT_TRACE_ME) {
        if (Process::current().tracer())
            return EBUSY;

        caller.set_wait_for_tracer_at_next_execve(true);
        return 0;
    }

    auto peer = Thread::from_tid_in_same_process_list(params.tid);
    if (!peer)
        return ESRCH;

    if (peer->pid() == caller.pid().value())
        return EPERM;

    MutexLocker ptrace_locker(peer->process().ptrace_lock());
    SpinlockLocker scheduler_lock(g_scheduler_lock);

    auto peer_credentials = peer->process().credentials();
    auto caller_credentials = caller.credentials();
    if (!caller_credentials->is_superuser() && ((peer_credentials->uid() != caller_credentials->euid()) || (peer_credentials->uid() != peer_credentials->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;
        }
        TRY(peer_process.start_tracing_from(caller.pid()));
        SpinlockLocker lock(peer->get_lock());
        if (peer->state() == Thread::State::Stopped) {
            peer_process.tracer()->set_regs(peer->get_register_dump_from_stack());
        } else {
            peer->send_signal(SIGSTOP, &caller);
        }