Skip to content

Commit

Permalink
Fix a race condition in FASYNC handling
Browse files Browse the repository at this point in the history
Changeset a238b79 (Call fasync()
functions without the BKL) introduced a race which could leave
file->f_flags in a state inconsistent with what the underlying
driver/filesystem believes.  Revert that change, and also fix the same
races in ioctl_fioasync() and ioctl_fionbio().

This is a minimal, short-term fix; the real fix will not involve the
BKL.

Reported-by: Oleg Nesterov <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: Al Viro <[email protected]>
Cc: [email protected]
Signed-off-by: Jonathan Corbet <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Jonathan Corbet authored and torvalds committed Dec 5, 2008
1 parent f2f1fa7 commit 218d11a
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 4 deletions.
7 changes: 7 additions & 0 deletions fs/fcntl.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <linux/signal.h>
#include <linux/rcupdate.h>
#include <linux/pid_namespace.h>
#include <linux/smp_lock.h>

#include <asm/poll.h>
#include <asm/siginfo.h>
Expand Down Expand Up @@ -175,6 +176,11 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
if (error)
return error;

/*
* We still need a lock here for now to keep multiple FASYNC calls
* from racing with each other.
*/
lock_kernel();
if ((arg ^ filp->f_flags) & FASYNC) {
if (filp->f_op && filp->f_op->fasync) {
error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
Expand All @@ -185,6 +191,7 @@ static int setfl(int fd, struct file * filp, unsigned long arg)

filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK);
out:
unlock_kernel();
return error;
}

Expand Down
12 changes: 8 additions & 4 deletions fs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,11 +400,9 @@ static int ioctl_fioasync(unsigned int fd, struct file *filp,

/* Did FASYNC state change ? */
if ((flag ^ filp->f_flags) & FASYNC) {
if (filp->f_op && filp->f_op->fasync) {
lock_kernel();
if (filp->f_op && filp->f_op->fasync)
error = filp->f_op->fasync(fd, filp, on);
unlock_kernel();
} else
else
error = -ENOTTY;
}
if (error)
Expand Down Expand Up @@ -440,11 +438,17 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
break;

case FIONBIO:
/* BKL needed to avoid races tweaking f_flags */
lock_kernel();
error = ioctl_fionbio(filp, argp);
unlock_kernel();
break;

case FIOASYNC:
/* BKL needed to avoid races tweaking f_flags */
lock_kernel();
error = ioctl_fioasync(fd, filp, argp);
unlock_kernel();
break;

case FIOQSIZE:
Expand Down

0 comments on commit 218d11a

Please sign in to comment.