Skip to content

Commit d08477a

Browse files
committed
fcntl: Don't use ambiguous SIG_POLL si_codes
We have a weird and problematic intersection of features that when they all come together result in ambiguous siginfo values, that we can not support properly. - Supporting fcntl(F_SETSIG,...) with arbitrary valid signals. - Using positive values for POLL_IN, POLL_OUT, POLL_MSG, ..., etc that imply they are signal specific si_codes and using the aforementioned arbitrary signal to deliver them. - Supporting injection of arbitrary siginfo values for debugging and checkpoint/restore. The result is that just looking at siginfo si_codes of 1 to 6 are ambigious. It could either be a signal specific si_code or it could be a generic si_code. For most of the kernel this is a non-issue but for sending signals with siginfo it is impossible to play back the kernel signals and get the same result. Strictly speaking when the si_code was changed from SI_SIGIO to POLL_IN and friends between 2.2 and 2.4 this functionality was not ambiguous, as only real time signals were supported. Before 2.4 was released the kernel began supporting siginfo with non realtime signals so they could give details of why the signal was sent. The result is that if F_SETSIG is set to one of the signals with signal specific si_codes then user space can not know why the signal was sent. I grepped through a bunch of userspace programs using debian code search to get a feel for how often people choose a signal that results in an ambiguous si_code. I only found one program doing so and it was using SIGCHLD to test the F_SETSIG functionality, and did not appear to be a real world usage. Therefore the ambiguity does not appears to be a real world problem in practice. Remove the ambiguity while introducing the smallest chance of breakage by changing the si_code to SI_SIGIO when signals with signal specific si_codes are targeted. Fixes: v2.3.40 -- Added support for queueing non-rt signals Fixes: v2.3.21 -- Changed the si_code from SI_SIGIO Signed-off-by: "Eric W. Biederman" <[email protected]>
1 parent d12fe87 commit d08477a

File tree

3 files changed

+22
-3
lines changed

3 files changed

+22
-3
lines changed

fs/fcntl.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,10 +741,21 @@ static void send_sigio_to_task(struct task_struct *p,
741741
si.si_signo = signum;
742742
si.si_errno = 0;
743743
si.si_code = reason;
744+
/*
745+
* Posix definies POLL_IN and friends to be signal
746+
* specific si_codes for SIG_POLL. Linux extended
747+
* these si_codes to other signals in a way that is
748+
* ambiguous if other signals also have signal
749+
* specific si_codes. In that case use SI_SIGIO instead
750+
* to remove the ambiguity.
751+
*/
752+
if (sig_specific_sicodes(signum))
753+
si.si_code = SI_SIGIO;
754+
744755
/* Make sure we are called with one of the POLL_*
745756
reasons, otherwise we could leak kernel stack into
746757
userspace. */
747-
BUG_ON((reason & __SI_MASK) != __SI_POLL);
758+
BUG_ON((reason < POLL_IN) || ((reason - POLL_IN) >= NSIGPOLL));
748759
if (reason - POLL_IN >= NSIGPOLL)
749760
si.si_band = ~0L;
750761
else

include/linux/signal.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,10 +380,18 @@ int unhandled_signal(struct task_struct *tsk, int sig);
380380
rt_sigmask(SIGCONT) | rt_sigmask(SIGCHLD) | \
381381
rt_sigmask(SIGWINCH) | rt_sigmask(SIGURG) )
382382

383+
#define SIG_SPECIFIC_SICODES_MASK (\
384+
rt_sigmask(SIGILL) | rt_sigmask(SIGFPE) | \
385+
rt_sigmask(SIGSEGV) | rt_sigmask(SIGBUS) | \
386+
rt_sigmask(SIGTRAP) | rt_sigmask(SIGCHLD) | \
387+
rt_sigmask(SIGPOLL) | rt_sigmask(SIGSYS) | \
388+
SIGEMT_MASK )
389+
383390
#define sig_kernel_only(sig) siginmask(sig, SIG_KERNEL_ONLY_MASK)
384391
#define sig_kernel_coredump(sig) siginmask(sig, SIG_KERNEL_COREDUMP_MASK)
385392
#define sig_kernel_ignore(sig) siginmask(sig, SIG_KERNEL_IGNORE_MASK)
386393
#define sig_kernel_stop(sig) siginmask(sig, SIG_KERNEL_STOP_MASK)
394+
#define sig_specific_sicodes(sig) siginmask(sig, SIG_SPECIFIC_SICODES_MASK)
387395

388396
#define sig_fatal(t, signr) \
389397
(!siginmask(signr, SIG_KERNEL_IGNORE_MASK|SIG_KERNEL_STOP_MASK) && \

include/uapi/asm-generic/siginfo.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ typedef struct siginfo {
184184
#define SI_TIMER __SI_CODE(__SI_TIMER,-2) /* sent by timer expiration */
185185
#define SI_MESGQ __SI_CODE(__SI_MESGQ,-3) /* sent by real time mesq state change */
186186
#define SI_ASYNCIO -4 /* sent by AIO completion */
187-
#define SI_SIGIO -5 /* sent by queued SIGIO */
187+
#define SI_SIGIO __SI_CODE(__SI_POLL,-5) /* sent by queued SIGIO */
188188
#define SI_TKILL -6 /* sent by tkill system call */
189189
#define SI_DETHREAD -7 /* sent by execve() killing subsidiary threads */
190190

@@ -259,7 +259,7 @@ typedef struct siginfo {
259259
#define NSIGCHLD 6
260260

261261
/*
262-
* SIGPOLL si_codes
262+
* SIGPOLL (or any other signal without signal specific si_codes) si_codes
263263
*/
264264
#define POLL_IN (__SI_POLL|1) /* data input available */
265265
#define POLL_OUT (__SI_POLL|2) /* output buffers available */

0 commit comments

Comments
 (0)