summaryrefslogtreecommitdiff
path: root/src/signal
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-07-30 21:11:31 -0400
committerRich Felker <dalias@aerifal.cx>2011-07-30 21:11:31 -0400
commit07827d1a82fb33262f686eda959857f0d28cd8fa (patch)
tree4a18e6e1ab1e9a96e23daaefd387d724556597e1 /src/signal
parentad5881842eaa6a04fcb35b822b0a046a3f7e73a2 (diff)
downloadmusl-07827d1a82fb33262f686eda959857f0d28cd8fa.tar.gz
fix race condition in sigqueue
this race is fundamentally due to linux's bogus requirement that userspace, rather than kernelspace, fill in the siginfo structure. an intervening signal handler that calls fork could cause both the parent and child process to send signals claiming to be from the parent, which could in turn have harmful effects depending on what the recipient does with the signal. we simply block all signals for the interval between getuid and sigqueue syscalls (much like what raise() does already) to prevent the race and make the getuid/sigqueue pair atomic. this will be a non-issue if linux is fixed to validate the siginfo structure or fill it in from kernelspace.
Diffstat (limited to 'src/signal')
-rw-r--r--src/signal/sigqueue.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/src/signal/sigqueue.c b/src/signal/sigqueue.c
index aeac4875..bdb12856 100644
--- a/src/signal/sigqueue.c
+++ b/src/signal/sigqueue.c
@@ -1,16 +1,22 @@
#include <signal.h>
#include <string.h>
#include <unistd.h>
+#include <stdint.h>
#include "syscall.h"
int sigqueue(pid_t pid, int sig, const union sigval value)
{
siginfo_t si;
+ sigset_t set;
+ int r;
memset(&si, 0, sizeof si);
si.si_signo = sig;
si.si_code = SI_QUEUE;
si.si_value = value;
- si.si_pid = getpid();
si.si_uid = getuid();
- return syscall(SYS_rt_sigqueueinfo, pid, sig, &si);
+ pthread_sigmask(SIG_BLOCK, (void *)(uint64_t[1]){-1}, &set);
+ si.si_pid = getpid();
+ r = syscall(SYS_rt_sigqueueinfo, pid, sig, &si);
+ pthread_sigmask(SIG_SETMASK, &set, 0);
+ return r;
}