From c21fc70e1a3c6b536aac7848f259ed68a8cf4b4c Mon Sep 17 00:00:00 2001 From: Michalis Pappas Date: Sun, 7 Jan 2024 11:56:16 +0100 Subject: [PATCH] lib/posix-process/signal: Add kill() and sigqueueinfo() Add syscalls for signalling. Internally these share common abstractions. The kill syscalls are used for sending signals to processes, process groups, or threads: * kill() allows sending a signal to a process group or process. * tkill() allows sending a signal to a thread. * tgkill() allows sending a signal to a thread in thread group. The siqeueueinfo syscalls are similar to kill but additionally allow to accompany the signal with data: * rt_sigqueueinfo() allows sending signal and data to a process. * rt_tgsigqueueinfo() allows sending signal and data to a thread in thread group. For more info see kill(2), tkill(2), rt_sigqueueinfo(2) Signed-off-by: Michalis Pappas Reviewed-by: Ioan-Teodor Teugea Reviewed-by: Sergiu Moga Reviewed-by: Andrei Tatar Approved-by: Andrei Tatar GitHub-Closes: #1248 --- lib/posix-process/Makefile.uk | 10 ++ lib/posix-process/exportsyms.uk | 2 + lib/posix-process/signal/kill.c | 24 +++ lib/posix-process/signal/rt_sigqueueinfo.c | 42 ++++++ lib/posix-process/signal/rt_tgsigqueueinfo.c | 56 +++++++ lib/posix-process/signal/siginfo.h | 44 ++++++ lib/posix-process/signal/signal.c | 148 +++++++++++++++++++ lib/posix-process/signal/signal.h | 9 ++ lib/posix-process/signal/tgkill.c | 34 +++++ lib/posix-process/signal/tkill.c | 23 +++ 10 files changed, 392 insertions(+) create mode 100644 lib/posix-process/signal/kill.c create mode 100644 lib/posix-process/signal/rt_sigqueueinfo.c create mode 100644 lib/posix-process/signal/rt_tgsigqueueinfo.c create mode 100644 lib/posix-process/signal/siginfo.h create mode 100644 lib/posix-process/signal/tgkill.c create mode 100644 lib/posix-process/signal/tkill.c diff --git a/lib/posix-process/Makefile.uk b/lib/posix-process/Makefile.uk index 7c4a61db6..7fcc5dec4 100644 --- a/lib/posix-process/Makefile.uk +++ b/lib/posix-process/Makefile.uk @@ -24,6 +24,11 @@ LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/signal/sigaltstack.c LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/signal/rt_sigpending.c LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/signal/rt_sigprocmask.c LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/signal/rt_sigaction.c +LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/signal/rt_sigqueueinfo.c +LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/signal/rt_tgsigqueueinfo.c +LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/signal/kill.c +LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/signal/tgkill.c +LIBPOSIX_PROCESS_SRCS-y += $(LIBPOSIX_PROCESS_BASE)/signal/tkill.c LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_CLONE) += $(LIBPOSIX_PROCESS_BASE)/clone.c LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_CLONE) += $(LIBPOSIX_PROCESS_BASE)/clonetab.ld @@ -41,6 +46,11 @@ UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += sigaltstack-2 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += rt_sigpending-2 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += rt_sigprocmask-4 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += rt_sigaction-4 +UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += rt_sigqueueinfo-3 +UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += rt_tgsigqueueinfo-4 +UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += kill-2 +UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += tgkill-3 +UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += tkill-2 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS_VFORK) += vfork-2e UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += wait4-4 waitid-4 UK_PROVIDED_SYSCALLS-$(CONFIG_LIBPOSIX_PROCESS) += getpgid-1 diff --git a/lib/posix-process/exportsyms.uk b/lib/posix-process/exportsyms.uk index 7a705d2c8..414d9bf4b 100644 --- a/lib/posix-process/exportsyms.uk +++ b/lib/posix-process/exportsyms.uk @@ -42,5 +42,7 @@ uk_posix_process_create uk_posix_process_kill clone vfork +kill sigaltstack sigprocmask +tgkill diff --git a/lib/posix-process/signal/kill.c b/lib/posix-process/signal/kill.c new file mode 100644 index 000000000..833b35633 --- /dev/null +++ b/lib/posix-process/signal/kill.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (c) 2023, Unikraft GmbH and The Unikraft Authors. + * Licensed under the BSD-3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + */ + +#include +#include +#include + +#include "signal.h" + +#if CONFIG_LIBPOSIX_PROCESS_SIGNAL +UK_SYSCALL_R_DEFINE(int, kill, pid_t, pid, int, signum) +{ + return pprocess_signal_process_do(pid, signum, NULL); +} +#else /* !CONFIG_LIBPOSIX_PROCESS_SIGNAL */ +UK_SYSCALL_R_DEFINE(int, kill, pid_t, pid, int, signum) +{ + UK_WARN_STUBBED(); + return 0; +} +#endif /* !CONFIG_LIBPOSIX_PROCESS_SIGNAL */ diff --git a/lib/posix-process/signal/rt_sigqueueinfo.c b/lib/posix-process/signal/rt_sigqueueinfo.c new file mode 100644 index 000000000..7cd23bbc2 --- /dev/null +++ b/lib/posix-process/signal/rt_sigqueueinfo.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (c) 2023, Unikraft GmbH and The Unikraft Authors. + * Licensed under the BSD-3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + */ + +#include +#include + +#include +#include +#include + +#include "signal.h" + +#if CONFIG_LIBPOSIX_PROCESS_SIGNAL +UK_LLSYSCALL_R_DEFINE(int, rt_sigqueueinfo, + int, tgid, + int, signum, + siginfo_t *, siginfo) +{ + if (unlikely(!siginfo)) + return -EFAULT; + + /* The si_code must be valid when targeting a + * different process. + */ + if (unlikely(tgid != uk_sys_getpid() && siginfo->si_code != SI_QUEUE)) + return -EPERM; + + return pprocess_signal_process_do(tgid, signum, siginfo); +} +#else /* !CONFIG_LIBPOSIX_PROCESS_SIGNAL */ +UK_LLSYSCALL_R_DEFINE(int, rt_sigqueueinfo, + int, tgid, + int, signum, + siginfo_t *, siginfo) +{ + UK_WARN_STUBBED(); + return 0; +} +#endif /* !CONFIG_LIBPOSIX_PROCESS_SIGNAL */ diff --git a/lib/posix-process/signal/rt_tgsigqueueinfo.c b/lib/posix-process/signal/rt_tgsigqueueinfo.c new file mode 100644 index 000000000..b5ad2e423 --- /dev/null +++ b/lib/posix-process/signal/rt_tgsigqueueinfo.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (c) 2023, Unikraft GmbH and The Unikraft Authors. + * Licensed under the BSD-3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + */ + +#include +#include + +#include +#include +#include + +#include "signal.h" + +#if CONFIG_LIBPOSIX_PROCESS_SIGNAL + +UK_LLSYSCALL_R_DEFINE(int, rt_tgsigqueueinfo, + int, tgid, int, tid, + int, signum, siginfo_t *, siginfo) +{ + struct posix_process *proc; + struct posix_thread *pthread; + + if (unlikely(!IS_VALID(signum))) + return -EINVAL; + + if (unlikely(!siginfo)) + return -EFAULT; + + pthread = tid2pthread(tid); + if (unlikely(!pthread)) + return -EINVAL; + + /* Make sure tid is part of by tgid */ + proc = tid2pprocess(tid); + if (unlikely(!proc || proc->pid != tgid)) + return -ESRCH; + + /* The si_code must be valid when targeting a + * different process. + */ + if (unlikely(siginfo->si_code != SI_QUEUE)) + return -EPERM; + + return pprocess_signal_thread_do(tid, signum, siginfo); +} +#else /* !CONFIG_LIBPOSIX_PROCESS_SIGNAL */ +UK_LLSYSCALL_R_DEFINE(int, rt_tgsigqueueinfo, + int, tgid, int, tid, + int, signum, siginfo_t *, siginfo) +{ + UK_WARN_STUBBED(); + return 0; +} +#endif /* !CONFIG_LIBPOSIX_PROCESS_SIGNAL */ diff --git a/lib/posix-process/signal/siginfo.h b/lib/posix-process/signal/siginfo.h new file mode 100644 index 000000000..43e3a8d4c --- /dev/null +++ b/lib/posix-process/signal/siginfo.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (c) 2023, Unikraft GmbH and The Unikraft Authors. + * Licensed under the BSD-3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + */ + +#ifndef __UK_PROCESS_SIGINFO_H__ +#define __UK_PROCESS_SIGINFO_H__ + +#include +#include +#include +#include + +#include "process.h" + +static inline void set_siginfo_kill(int signum, siginfo_t *si) +{ + UK_ASSERT(si); + + si->si_signo = signum; + si->si_code = SI_USER; + si->si_pid = uk_sys_getpid(); + si->si_uid = 0; +} + +static inline void set_siginfo_sigqueue(int signum, siginfo_t *si, + siginfo_t *si_usr) +{ + UK_ASSERT(si); + UK_ASSERT(si_usr); + + si->si_signo = signum; + + /* The remaining fields are set by the caller + * without verification. + */ + si->si_code = si_usr->si_code; + si->si_pid = si_usr->si_pid; + si->si_uid = si_usr->si_uid; + si->si_value = si_usr->si_value; +} + +#endif /* __UK_PROCESS_SIGINFO_H__ */ diff --git a/lib/posix-process/signal/signal.c b/lib/posix-process/signal/signal.c index 924f449c1..0620b4cba 100644 --- a/lib/posix-process/signal/signal.c +++ b/lib/posix-process/signal/signal.c @@ -10,8 +10,18 @@ #include "process.h" #include "sigset.h" +#include "siginfo.h" #include "signal.h" +/* Check permissions to signal target process. + * + * For a process to be permitted to send a signal to another + * process, its real / effective uid must match the target's + * real / saved set-user-id. We don't support these, so we + * deliver to every process. + */ +#define signal_check_perm(_target_process) true + /* Enqueue signal for a thread or a process * * Setting pthread to NULL signifies that the signal @@ -161,6 +171,144 @@ int pprocess_signal_sigaction_new(struct uk_alloc *alloc, struct uk_signal_pdesc return 0; } +int pprocess_signal_send(struct posix_process *proc, int signum, + siginfo_t *siginfo) +{ + struct uk_signal *sig; + int rc; + + sig = uk_signal_alloc(proc->_a); + if (unlikely(!sig)) { + uk_pr_err("Could not allocate signal\n"); + return -EAGAIN; + } + + if (siginfo) + set_siginfo_sigqueue(signum, &sig->siginfo, siginfo); + else + set_siginfo_kill(signum, &sig->siginfo); + + rc = pprocess_signal_enqueue(proc, NULL, sig); + if (unlikely(rc)) { + /* issue a warning as this may be temporary */ + uk_pr_warn("Could not queue signal\n"); + uk_signal_free(proc->_a, sig); + return -EAGAIN; + } + + return 0; +} + +int pprocess_signal_process_do(pid_t pid, int signum, siginfo_t *siginfo) +{ + struct posix_process *pproc = NULL; + struct posix_thread *thread, *threadn; + int rc; + + if (unlikely(signum != 0 && !IS_VALID(signum))) + return -EINVAL; + + if (unlikely(signum == SIGSTOP || signum == SIGCONT)) { + uk_pr_warn("Process stop / resume not supported. Ignoring\n"); + return 0; + } + + /* pid == 0 -> Send the signal to every process in the process group + * of the calling process. + * pid < -1 -> Send the signal to every process in the process group + * with ID equal to -pid. + */ + if (!pid || pid < -1) { + uk_pr_warn("pgroups not supported, delivering to every process\n"); + pid = -1; + } + + /* pid == -1 -> Send the signal to every process for which the + * calling process has permissions to signal. + */ + if (pid == -1) { + uk_pprocess_foreach(pproc) { + if (!signal_check_perm(pproc)) + continue; + + if (IS_IGNORED(pproc, signum)) + continue; + + rc = pprocess_signal_send(pproc, signum, siginfo); + if (unlikely(rc)) + return rc; + } + return 0; + } + + /* Default: Send to process with pid */ + pproc = pid2pprocess(pid); + if (unlikely(!pproc)) + return -ESRCH; + + if (unlikely(signal_check_perm(pproc) == false)) + return -EPERM; + + /* If signum == 0 do only pid exist & permissions check */ + if (!signum) + return 0; + + /* If this signal is currently ignored, don't even try */ + if (IS_IGNORED(pproc, signum)) + return 0; + + rc = pprocess_signal_send(pproc, signum, siginfo); + if (unlikely(rc)) + return rc; + + /* Wake up any threads that may be paused */ + uk_pprocess_foreach_pthread(pproc, thread, threadn) { + if (thread->state == POSIX_THREAD_BLOCKED_SIGNAL) + uk_semaphore_up(&thread->signal->deliver_semaphore); + } + + return 0; +} + +int pprocess_signal_thread_do(int tid, int signum, siginfo_t *siginfo) +{ + struct posix_thread *pthread; + struct posix_process *pproc; + struct uk_signal *sig; + int rc; + + pthread = tid2pthread(tid); + if (unlikely(!pthread)) + return -EINVAL; + + pproc = tid2pprocess(tid); + UK_ASSERT(pproc); + + sig = uk_signal_alloc(pthread->_a); + if (unlikely(!sig)) { + uk_pr_err("Could not allocate signal\n"); + return -EAGAIN; + } + + if (siginfo) + set_siginfo_sigqueue(signum, &sig->siginfo, siginfo); + else + set_siginfo_kill(signum, &sig->siginfo); + + rc = pprocess_signal_enqueue(pproc, pthread, sig); + if (unlikely(rc)) { + /* issue a warning as this may be temporary */ + uk_pr_warn("Could not queue signal\n"); + uk_signal_free(pthread->_a, sig); + return rc; + } + + if (pthread->state == POSIX_THREAD_BLOCKED_SIGNAL) + uk_semaphore_up(&pthread->signal->deliver_semaphore); + + return 0; +} + int pprocess_signal_pdesc_alloc(struct posix_process *process) { UK_ASSERT(process); diff --git a/lib/posix-process/signal/signal.h b/lib/posix-process/signal/signal.h index d1695ed98..fbb7a3d78 100644 --- a/lib/posix-process/signal/signal.h +++ b/lib/posix-process/signal/signal.h @@ -252,6 +252,15 @@ void pprocess_signal_clear_pending(struct posix_process *proc, int signum); /* Get next pending signal of a given thread */ struct uk_signal *pprocess_signal_next_pending_t(struct posix_thread *pthread); +/* Signal a process (abstraction for kill / sigqueueinfo) */ +int pprocess_signal_process_do(pid_t pid, int signum, siginfo_t *siginfo); + +/* Signal a thread (abstraction for kill / sigqueueinfo) */ +int pprocess_signal_thread_do(int tid, int signum, siginfo_t *siginfo); + +/* Signal a process */ +int pprocess_signal_send(struct posix_process *proc, int signum, + siginfo_t *siginfo); #endif /* CONFIG_LIBPOSIX_PROCESS_PIDS */ #endif /* __UK_PROCESS_SIGNAL_H__ */ diff --git a/lib/posix-process/signal/tgkill.c b/lib/posix-process/signal/tgkill.c new file mode 100644 index 000000000..e98e8aa40 --- /dev/null +++ b/lib/posix-process/signal/tgkill.c @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (c) 2023, Unikraft GmbH and The Unikraft Authors. + * Licensed under the BSD-3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + */ + +#include + +#include +#include + +#include "signal.h" + +#if CONFIG_LIBPOSIX_PROCESS_SIGNAL +UK_SYSCALL_R_DEFINE(int, tgkill, int, tgid, + int, tid, int, signum) +{ + struct posix_process *pproc; + + /* Chack tid is part of my tgid */ + pproc = tid2pprocess(tid); + if (unlikely(!pproc || pproc->pid != tgid)) + return -ESRCH; + + return pprocess_signal_thread_do(tid, signum, NULL); +} +#else /* !CONFIG_LIBPOSIX_PROCESS_SIGNAL */ +UK_SYSCALL_R_DEFINE(int, tgkill, int, tgid, + int, tid, int, signum) +{ + UK_WARN_STUBBED(); + return 0; +} +#endif /* !CONFIG_LIBPOSIX_PROCESS_SIGNAL */ diff --git a/lib/posix-process/signal/tkill.c b/lib/posix-process/signal/tkill.c new file mode 100644 index 000000000..27aab5f9a --- /dev/null +++ b/lib/posix-process/signal/tkill.c @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (c) 2023, Unikraft GmbH and The Unikraft Authors. + * Licensed under the BSD-3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + */ + +#include +#include + +#include "signal.h" + +#if CONFIG_LIBPOSIX_PROCESS_SIGNAL +UK_LLSYSCALL_R_DEFINE(int, tkill, int, tid, int, signum) +{ + return pprocess_signal_thread_do(tid, signum, NULL); +} +#else /* !CONFIG_LIBPOSIX_PROCESS_SIGNAL */ +UK_LLSYSCALL_R_DEFINE(int, tkill, int, tid, int, signum) +{ + UK_WARN_STUBBED(); + return 0; +} +#endif /* !CONFIG_LIBPOSIX_PROCESS_SIGNAL */ -- 2.39.5