summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2009-06-17 00:27:10 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2009-06-16 15:36:17 -0700
commit8eeee4e2f04fc551f50c9d9847da2d73d7d33728 (patch)
treec5b7a2c9b912369c82506f8363b8eb10538f5d8e
parentf83b1e616f2f68b56b09b2f5116591981fee0c1c (diff)
send_sigio_to_task: sanitize the usage of fown->signum
send_sigio_to_task() reads fown->signum several times, we can race with F_SETSIG which changes ->signum lockless. In theory, this can fool security checks or we can call group_send_sig_info() with the wrong ->si_signo which does not match "int sig". Change the code to cache ->signum. Signed-off-by: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/fcntl.c16
1 files changed, 11 insertions, 5 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c
index f9c03ca3b2f4..a040b764f8e3 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -428,14 +428,20 @@ static inline int sigio_perm(struct task_struct *p,
}
static void send_sigio_to_task(struct task_struct *p,
- struct fown_struct *fown,
+ struct fown_struct *fown,
int fd,
int reason)
{
- if (!sigio_perm(p, fown, fown->signum))
+ /*
+ * F_SETSIG can change ->signum lockless in parallel, make
+ * sure we read it once and use the same value throughout.
+ */
+ int signum = ACCESS_ONCE(fown->signum);
+
+ if (!sigio_perm(p, fown, signum))
return;
- switch (fown->signum) {
+ switch (signum) {
siginfo_t si;
default:
/* Queue a rt signal with the appropriate fd as its
@@ -444,7 +450,7 @@ static void send_sigio_to_task(struct task_struct *p,
delivered even if we can't queue. Failure to
queue in this case _should_ be reported; we fall
back to SIGIO in that case. --sct */
- si.si_signo = fown->signum;
+ si.si_signo = signum;
si.si_errno = 0;
si.si_code = reason;
/* Make sure we are called with one of the POLL_*
@@ -456,7 +462,7 @@ static void send_sigio_to_task(struct task_struct *p,
else
si.si_band = band_table[reason - POLL_IN];
si.si_fd = fd;
- if (!group_send_sig_info(fown->signum, &si, p))
+ if (!group_send_sig_info(signum, &si, p))
break;
/* fall-through: fall back on the old plain SIGIO signal */
case 0: