/* * Copyright (c) 2018-2023, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include namespace Kernel { ErrorOr Process::do_kill(Process& process, int signal) { // FIXME: Should setuid processes have some special treatment here? auto credentials = this->credentials(); auto kill_process_credentials = process.credentials(); bool can_send_signal = credentials->is_superuser() || credentials->euid() == kill_process_credentials->uid() || credentials->uid() == kill_process_credentials->uid() || (signal == SIGCONT && credentials->pgid() == kill_process_credentials->pgid()); if (!can_send_signal) return EPERM; if (process.is_kernel_process()) { process.name().with([&](auto& process_name) { dbgln("Attempted to send signal {} to kernel process {} ({})", signal, process_name->view(), process.pid()); }); return EPERM; } if (signal != 0) return process.send_signal(signal, this); return {}; } ErrorOr Process::do_killpg(ProcessGroupID pgrp, int signal) { VERIFY(pgrp >= 0); // Send the signal to all processes in the given group. if (pgrp == 0) { // Send the signal to our own pgrp. pgrp = pgid(); } bool group_was_empty = true; bool any_succeeded = false; ErrorOr error; TRY(Process::current().for_each_in_pgrp_in_same_jail(pgrp, [&](auto& process) -> ErrorOr { group_was_empty = false; ErrorOr res = do_kill(process, signal); if (!res.is_error()) any_succeeded = true; else error = move(res); return {}; })); if (group_was_empty) return ESRCH; if (any_succeeded) return {}; return error; } ErrorOr Process::do_killall(int signal) { bool any_succeeded = false; ErrorOr error; // Send the signal to all processes we have access to for. TRY(Process::for_each_in_same_jail([&](auto& process) -> ErrorOr { ErrorOr res; if (process.pid() == pid()) res = do_killself(signal); else res = do_kill(process, signal); if (!res.is_error()) any_succeeded = true; else error = move(res); return {}; })); if (any_succeeded) return {}; return error; } ErrorOr Process::do_killself(int signal) { if (signal == 0) return {}; auto* current_thread = Thread::current(); if (!current_thread->should_ignore_signal(signal)) current_thread->send_signal(signal, this); return {}; } ErrorOr Process::sys$kill(pid_t pid_or_pgid, int signal) { VERIFY_NO_PROCESS_BIG_LOCK(this); if (pid_or_pgid == pid().value()) TRY(require_promise(Pledge::stdio)); else TRY(require_promise(Pledge::proc)); if (signal < 0 || signal >= 32) return EINVAL; if (pid_or_pgid < -1) { if (pid_or_pgid == NumericLimits::min()) return EINVAL; TRY(do_killpg(-pid_or_pgid, signal)); return 0; } if (pid_or_pgid == -1) { TRY(do_killall(signal)); return 0; } if (pid_or_pgid == pid().value()) { TRY(do_killself(signal)); return 0; } VERIFY(pid_or_pgid >= 0); auto peer = Process::from_pid_in_same_jail(pid_or_pgid); if (!peer) return ESRCH; TRY(do_kill(*peer, signal)); return 0; } ErrorOr Process::sys$killpg(pid_t pgrp, int signum) { VERIFY_NO_PROCESS_BIG_LOCK(this); TRY(require_promise(Pledge::proc)); if (signum < 1 || signum >= 32) return EINVAL; if (pgrp < 0) return EINVAL; TRY(do_killpg(pgrp, signum)); return 0; } }