Check if process is allowed to send signal
MODIFY
include/sys/cred.h
include/sys/cred.h
#include <sys/types.h>
#include <sys/syslimits.h>
+#include <sys/signal.h>
#ifdef _KERNEL
@@ -41,6 +42,14 @@ int do_setregid(proc_t *p, gid_t rgid, gid_t egid);
*/
void cred_fork(proc_t *to, proc_t *from);
+/* \note Must be called with p::p_lock. Returns p::p_lock held. */
+int cred_cansignal(proc_t *p, cred_t *cred);
+
+/* Checks whether the current process has permission to send `sig` to `target`
+ * process. \note Must be called with all_proc_mtx and target::p_lock. Returns
+ * with lock held. */
+int proc_cansignal(proc_t *target, signo_t sig);
+
#endif /* !_KERNEL */
#endif /* _SYS_CRED_H_ */

MODIFY
include/sys/proc.h
include/sys/proc.h
@@ -91,7 +91,7 @@ struct proc {
TAILQ_ENTRY(proc) p_hash; /* (a) link on pid hash chain */
thread_t *p_thread; /* (@) the only thread running in this process */
pid_t p_pid; /* (!) Process ID */
- cred_t p_cred; /* (@) Process credentials */
+ cred_t p_cred; /* (@, *) Process credentials */
char *p_elfpath; /* (!) path of loaded elf file */
TAILQ_ENTRY(proc) p_pglist; /* (g + a) link on pg_members list */
pgrp_t *p_pgrp; /* (@ + a) process group */
@@ -140,6 +140,7 @@ void proc_add(proc_t *p);
proc_t *proc_find(pid_t pid);
/*! \brief Sends signal to process group or process.
+ * Signal is send on behalf of the current process. Performs privilege checks.
* (pid > 0) sends signal to the process with the ID specified by pid.
* (pid = 0) sends signal to processes in process group of the calling process.
* (pid <-1) sends signal to processes in process group with ID equal (-pid). */

MODIFY
sys/kern/Makefile
sys/kern/Makefile
@@ -11,6 +11,7 @@ SOURCES = \
condvar.c \
console.c \
cred.c \
+ cred_checks.c \
cred_syscalls.c \
devclass.c \
device.c \

ADD
sys/kern/cred_checks.c
+#include <sys/cred.h>
+#include <sys/proc.h>
+#include <sys/errno.h>
+#include <sys/mutex.h>
+
+int cred_cansignal(proc_t *target, cred_t *cred) {
+ assert(mtx_owned(&target->p_lock));
+
+ if (cred->cr_euid == 0)
+ return 0;
+
+ if (cred->cr_ruid != target->p_cred.cr_ruid &&
+ cred->cr_ruid != target->p_cred.cr_suid &&
+ cred->cr_euid != target->p_cred.cr_ruid &&
+ cred->cr_euid != target->p_cred.cr_suid)
+ return EPERM;
+
+ return 0;
+}
+
+int proc_cansignal(proc_t *target, signo_t sig) {
+ assert(mtx_owned(all_proc_mtx));
+ assert(mtx_owned(&target->p_lock));
+
+ proc_t *p = proc_self();
+
+ if (target == p)
+ return 0;
+
+ /* process can send SIGCONT to every process in the same session */
+ if (sig == SIGCONT && p->p_pgrp->pg_session == target->p_pgrp->pg_session)
+ return 0;
+
+ return cred_cansignal(target, &p->p_cred);
+}

MODIFY
sys/kern/proc.c
sys/kern/proc.c
@@ -590,42 +590,66 @@ __noreturn void proc_exit(int exitstatus) {
thread_exit();
}
+static int proc_pgsignal(pgid_t pgid, signo_t sig) {
+ pgrp_t *pgrp = NULL;
+ SCOPED_MTX_LOCK(all_proc_mtx);
+
+ if (pgid == 0) {
+ pgrp = proc_self()->p_pgrp;
+ } else {
+ pgrp = pgrp_lookup(pgid);
+ if (!pgrp)
+ return ESRCH;
+ }
+
+ proc_t *target;
+ int send = 0, error = 0;
+ TAILQ_FOREACH (target, &pgrp->pg_members, p_pglist) {
+ WITH_PROC_LOCK(target) {
+ if (!(error = proc_cansignal(target, sig))) {
+ sig_kill(target, sig);
+ send++;
+ }
+ }
+ }
+
+ /* We return error when signal can't be send to any process. Returned error is
+ * last error obtained from checking privileges.*/
+ return send > 0 ? 0 : error;
+}
+
int proc_sendsig(pid_t pid, signo_t sig) {
+ if (sig >= NSIG)
+ return EINVAL;
+
+ int error;
proc_t *target;
if (pid > 0) {
- WITH_MTX_LOCK (all_proc_mtx)
+ SCOPED_MTX_LOCK(all_proc_mtx);
target = proc_find(pid);
if (target == NULL)
- return EINVAL;
+ return ESRCH;
+ if (!(error = proc_cansignal(target, sig)))
sig_kill(target, sig);
proc_unlock(target);
- return 0;
+ return error;
}
+ switch (pid) {
+ case -1:
/* TODO send sig to every process for which the calling process has
* permission to send signals, except init process */
- if (pid == -1)
- return ENOTSUP;
-
- pgrp_t *pgrp = NULL;
-
- WITH_MTX_LOCK (all_proc_mtx) {
- if (pid == 0)
- pgrp = proc_self()->p_pgrp;
-
- if (pid < -1) {
- pgrp = pgrp_lookup(-pid);
- if (!pgrp)
- return EINVAL;
- }
- mtx_lock(&pgrp->pg_lock);
+ error = ENOTSUP;
+ break;
+ case 0:
+ error = proc_pgsignal(0, sig);
+ break;
+ default:
+ error = proc_pgsignal(-pid, sig);
}
-
- sig_pgkill(pgrp, sig);
- mtx_unlock(&pgrp->pg_lock);
- return 0;
+ return error;
}
static bool is_zombie(proc_t *p) {

Check if process is allowed to send signal (#802)