Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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" <ebiederm@xmission.com>

+22 -3
+12 -1
fs/fcntl.c
··· 741 741 si.si_signo = signum; 742 742 si.si_errno = 0; 743 743 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 + 744 755 /* Make sure we are called with one of the POLL_* 745 756 reasons, otherwise we could leak kernel stack into 746 757 userspace. */ 747 - BUG_ON((reason & __SI_MASK) != __SI_POLL); 758 + BUG_ON((reason < POLL_IN) || ((reason - POLL_IN) >= NSIGPOLL)); 748 759 if (reason - POLL_IN >= NSIGPOLL) 749 760 si.si_band = ~0L; 750 761 else
+8
include/linux/signal.h
··· 380 380 rt_sigmask(SIGCONT) | rt_sigmask(SIGCHLD) | \ 381 381 rt_sigmask(SIGWINCH) | rt_sigmask(SIGURG) ) 382 382 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 + 383 390 #define sig_kernel_only(sig) siginmask(sig, SIG_KERNEL_ONLY_MASK) 384 391 #define sig_kernel_coredump(sig) siginmask(sig, SIG_KERNEL_COREDUMP_MASK) 385 392 #define sig_kernel_ignore(sig) siginmask(sig, SIG_KERNEL_IGNORE_MASK) 386 393 #define sig_kernel_stop(sig) siginmask(sig, SIG_KERNEL_STOP_MASK) 394 + #define sig_specific_sicodes(sig) siginmask(sig, SIG_SPECIFIC_SICODES_MASK) 387 395 388 396 #define sig_fatal(t, signr) \ 389 397 (!siginmask(signr, SIG_KERNEL_IGNORE_MASK|SIG_KERNEL_STOP_MASK) && \
+2 -2
include/uapi/asm-generic/siginfo.h
··· 184 184 #define SI_TIMER __SI_CODE(__SI_TIMER,-2) /* sent by timer expiration */ 185 185 #define SI_MESGQ __SI_CODE(__SI_MESGQ,-3) /* sent by real time mesq state change */ 186 186 #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 */ 188 188 #define SI_TKILL -6 /* sent by tkill system call */ 189 189 #define SI_DETHREAD -7 /* sent by execve() killing subsidiary threads */ 190 190 ··· 259 259 #define NSIGCHLD 6 260 260 261 261 /* 262 - * SIGPOLL si_codes 262 + * SIGPOLL (or any other signal without signal specific si_codes) si_codes 263 263 */ 264 264 #define POLL_IN (__SI_POLL|1) /* data input available */ 265 265 #define POLL_OUT (__SI_POLL|2) /* output buffers available */