summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2017-07-24 14:39:37 -0500
committerEric W. Biederman <ebiederm@xmission.com>2017-07-24 14:39:37 -0500
commit64a76d0d64bea159da997c002a916ffc03f98bfc (patch)
treef37d919ba5cc075ffa118fdf96cc3af33d8764f1 /fs
parent4d28df6152aa3ffd0ad0389bb1d31f5b1c1c2b1f (diff)
parentcc731525f26af85a1c3537da41e0abd1d35e0bdb (diff)
signal: Fix sending signals with siginfo
Today sending a signal with rt_sigqueueinfo and receving it on a signalfd does not work reliably. The issue is that reading a signalfd instead of returning a siginfo returns a signalfd_siginfo and the kernel must convert from one to the other. The kernel does not currently have the code to deduce which union members of struct siginfo are in use. In this patchset I fix that by introducing a new function siginfo_layout that can look at a siginfo and report which union member of struct siginfo is in use. Before that I clean up how we populate struct siginfo. The siginfo structure has two key members si_signo and si_code. Some si_codes are signal specific and for those it takes si_signo and si_code to indicate the members of siginfo that are valid. The rest of the si_code values are signal independent like SI_USER, SI_KERNEL, SI_QUEUE, and SI_TIMER and only si_code is needed to indicate which members of siginfo are valid. At least that is how POSIX documents them, and how common sense would indicate they should function. In practice we have been rather sloppy about maintaining the ABI in linux and we have some exceptions. We have a couple of buggy architectures that make SI_USER mean something different when combined with SIGFPE or SIGTRAP. Worse we have fcntl(F_SETSIG) which results in the si_codes POLL_IN, POLL_OUT, POLL_MSG, POLL_ERR, POLL_PRI, POLL_HUP being sent with any arbitrary signal, while the values are in a range that overlaps the signal specific si_codes. Thankfully the ambiguous cases with the POLL_NNN si_codes are for things no sane persion would do that so we can rectify the situtation. AKA no one cares so we won't cause a regression fixing it. As part of fixing this I stop leaking the __SI_xxxx codes to userspace and stop storing them in the high 16bits of si_code. Making the kernel code fundamentally simpler. We have already confirmed that the one application that would see this difference in kernel behavior CRIU won't be affected by this change as it copies values verbatim from one kernel interface to another. v3: - Corrected the patches so they bisect properly v2: - Benchmarked the code to confirm no performance changes are visible. - Reworked the first couple of patches so that TRAP_FIXME and FPE_FIXME are not exported to userspace. - Rebased on top of the siginfo cleanup that came in v4.13-rc1 - Updated alpha to use both TRAP_FIXME and FPE_FIXME Eric W. Biederman (7): signal/alpha: Document a conflict with SI_USER for SIGTRAP signal/ia64: Document a conflict with SI_USER with SIGFPE signal/sparc: Document a conflict with SI_USER with SIGFPE signal/mips: Document a conflict with SI_USER with SIGFPE signal/testing: Don't look for __SI_FAULT in userspace fcntl: Don't use ambiguous SIG_POLL si_codes signal: Remove kernel interal si_code magic Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/fcntl.c13
-rw-r--r--fs/signalfd.c22
2 files changed, 20 insertions, 15 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 3b01b646e528..0491da3b28c3 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -741,10 +741,21 @@ static void send_sigio_to_task(struct task_struct *p,
si.si_signo = signum;
si.si_errno = 0;
si.si_code = reason;
+ /*
+ * Posix definies POLL_IN and friends to be signal
+ * specific si_codes for SIG_POLL. Linux extended
+ * these si_codes to other signals in a way that is
+ * ambiguous if other signals also have signal
+ * specific si_codes. In that case use SI_SIGIO instead
+ * to remove the ambiguity.
+ */
+ if (sig_specific_sicodes(signum))
+ si.si_code = SI_SIGIO;
+
/* Make sure we are called with one of the POLL_*
reasons, otherwise we could leak kernel stack into
userspace. */
- BUG_ON((reason & __SI_MASK) != __SI_POLL);
+ BUG_ON((reason < POLL_IN) || ((reason - POLL_IN) >= NSIGPOLL));
if (reason - POLL_IN >= NSIGPOLL)
si.si_band = ~0L;
else
diff --git a/fs/signalfd.c b/fs/signalfd.c
index 593b022ac11b..d2c434112f42 100644
--- a/fs/signalfd.c
+++ b/fs/signalfd.c
@@ -95,23 +95,23 @@ static int signalfd_copyinfo(struct signalfd_siginfo __user *uinfo,
*/
err |= __put_user(kinfo->si_signo, &uinfo->ssi_signo);
err |= __put_user(kinfo->si_errno, &uinfo->ssi_errno);
- err |= __put_user((short) kinfo->si_code, &uinfo->ssi_code);
- switch (kinfo->si_code & __SI_MASK) {
- case __SI_KILL:
+ err |= __put_user(kinfo->si_code, &uinfo->ssi_code);
+ switch (siginfo_layout(kinfo->si_signo, kinfo->si_code)) {
+ case SIL_KILL:
err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid);
err |= __put_user(kinfo->si_uid, &uinfo->ssi_uid);
break;
- case __SI_TIMER:
+ case SIL_TIMER:
err |= __put_user(kinfo->si_tid, &uinfo->ssi_tid);
err |= __put_user(kinfo->si_overrun, &uinfo->ssi_overrun);
err |= __put_user((long) kinfo->si_ptr, &uinfo->ssi_ptr);
err |= __put_user(kinfo->si_int, &uinfo->ssi_int);
break;
- case __SI_POLL:
+ case SIL_POLL:
err |= __put_user(kinfo->si_band, &uinfo->ssi_band);
err |= __put_user(kinfo->si_fd, &uinfo->ssi_fd);
break;
- case __SI_FAULT:
+ case SIL_FAULT:
err |= __put_user((long) kinfo->si_addr, &uinfo->ssi_addr);
#ifdef __ARCH_SI_TRAPNO
err |= __put_user(kinfo->si_trapno, &uinfo->ssi_trapno);
@@ -128,20 +128,14 @@ static int signalfd_copyinfo(struct signalfd_siginfo __user *uinfo,
&uinfo->ssi_addr_lsb);
#endif
break;
- case __SI_CHLD:
+ case SIL_CHLD:
err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid);
err |= __put_user(kinfo->si_uid, &uinfo->ssi_uid);
err |= __put_user(kinfo->si_status, &uinfo->ssi_status);
err |= __put_user(kinfo->si_utime, &uinfo->ssi_utime);
err |= __put_user(kinfo->si_stime, &uinfo->ssi_stime);
break;
- case __SI_RT: /* This is not generated by the kernel as of now. */
- case __SI_MESGQ: /* But this is */
- err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid);
- err |= __put_user(kinfo->si_uid, &uinfo->ssi_uid);
- err |= __put_user((long) kinfo->si_ptr, &uinfo->ssi_ptr);
- err |= __put_user(kinfo->si_int, &uinfo->ssi_int);
- break;
+ case SIL_RT:
default:
/*
* This case catches also the signals queued by sigqueue().