diff options
author | Oleg Nesterov <oleg@redhat.com> | 2009-06-16 18:27:10 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-16 18:36:17 -0400 |
commit | 8eeee4e2f04fc551f50c9d9847da2d73d7d33728 (patch) | |
tree | c5b7a2c9b912369c82506f8363b8eb10538f5d8e | |
parent | f83b1e616f2f68b56b09b2f5116591981fee0c1c (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.c | 16 |
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, | |||
428 | } | 428 | } |
429 | 429 | ||
430 | static void send_sigio_to_task(struct task_struct *p, | 430 | static void send_sigio_to_task(struct task_struct *p, |
431 | struct fown_struct *fown, | 431 | struct fown_struct *fown, |
432 | int fd, | 432 | int fd, |
433 | int reason) | 433 | int reason) |
434 | { | 434 | { |
435 | if (!sigio_perm(p, fown, fown->signum)) | 435 | /* |
436 | * F_SETSIG can change ->signum lockless in parallel, make | ||
437 | * sure we read it once and use the same value throughout. | ||
438 | */ | ||
439 | int signum = ACCESS_ONCE(fown->signum); | ||
440 | |||
441 | if (!sigio_perm(p, fown, signum)) | ||
436 | return; | 442 | return; |
437 | 443 | ||
438 | switch (fown->signum) { | 444 | switch (signum) { |
439 | siginfo_t si; | 445 | siginfo_t si; |
440 | default: | 446 | default: |
441 | /* Queue a rt signal with the appropriate fd as its | 447 | /* Queue a rt signal with the appropriate fd as its |
@@ -444,7 +450,7 @@ static void send_sigio_to_task(struct task_struct *p, | |||
444 | delivered even if we can't queue. Failure to | 450 | delivered even if we can't queue. Failure to |
445 | queue in this case _should_ be reported; we fall | 451 | queue in this case _should_ be reported; we fall |
446 | back to SIGIO in that case. --sct */ | 452 | back to SIGIO in that case. --sct */ |
447 | si.si_signo = fown->signum; | 453 | si.si_signo = signum; |
448 | si.si_errno = 0; | 454 | si.si_errno = 0; |
449 | si.si_code = reason; | 455 | si.si_code = reason; |
450 | /* Make sure we are called with one of the POLL_* | 456 | /* Make sure we are called with one of the POLL_* |
@@ -456,7 +462,7 @@ static void send_sigio_to_task(struct task_struct *p, | |||
456 | else | 462 | else |
457 | si.si_band = band_table[reason - POLL_IN]; | 463 | si.si_band = band_table[reason - POLL_IN]; |
458 | si.si_fd = fd; | 464 | si.si_fd = fd; |
459 | if (!group_send_sig_info(fown->signum, &si, p)) | 465 | if (!group_send_sig_info(signum, &si, p)) |
460 | break; | 466 | break; |
461 | /* fall-through: fall back on the old plain SIGIO signal */ | 467 | /* fall-through: fall back on the old plain SIGIO signal */ |
462 | case 0: | 468 | case 0: |