Skip to content

Commit

Permalink
[PATCH] fix SMP ordering hole in fcntl_setlk()
Browse files Browse the repository at this point in the history
fcntl_setlk()/close() race prevention has a subtle hole - we need to
make sure that if we *do* have an fcntl/close race on SMP box, the
access to descriptor table and inode->i_flock won't get reordered.

As it is, we get STORE inode->i_flock, LOAD descriptor table entry vs.
STORE descriptor table entry, LOAD inode->i_flock with not a single
lock in common on both sides.  We do have BKL around the first STORE,
but check in locks_remove_posix() is outside of BKL and for a good
reason - we don't want BKL on common path of close(2).

Solution is to hold ->file_lock around fcheck() in there; that orders
us wrt removal from descriptor table that preceded locks_remove_posix()
on close path and we either come first (in which case eviction will be
handled by the close side) or we'll see the effect of close and do
eviction ourselves.  Note that even though it's read-only access,
we do need ->file_lock here - rcu_read_lock() won't be enough to
order the things.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
  • Loading branch information
Al Viro committed May 6, 2008
1 parent 33dcdac commit 0b2bac2
Showing 1 changed file with 15 additions and 2 deletions.
17 changes: 15 additions & 2 deletions fs/locks.c
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,7 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
struct file_lock *file_lock = locks_alloc_lock();
struct flock flock;
struct inode *inode;
struct file *f;
int error;

if (file_lock == NULL)
Expand Down Expand Up @@ -1825,7 +1826,15 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
* Attempt to detect a close/fcntl race and recover by
* releasing the lock that was just acquired.
*/
if (!error && fcheck(fd) != filp && flock.l_type != F_UNLCK) {
/*
* we need that spin_lock here - it prevents reordering between
* update of inode->i_flock and check for it done in close().
* rcu_read_lock() wouldn't do.
*/
spin_lock(&current->files->file_lock);
f = fcheck(fd);
spin_unlock(&current->files->file_lock);
if (!error && f != filp && flock.l_type != F_UNLCK) {
flock.l_type = F_UNLCK;
goto again;
}
Expand Down Expand Up @@ -1881,6 +1890,7 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
struct file_lock *file_lock = locks_alloc_lock();
struct flock64 flock;
struct inode *inode;
struct file *f;
int error;

if (file_lock == NULL)
Expand Down Expand Up @@ -1953,7 +1963,10 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
* Attempt to detect a close/fcntl race and recover by
* releasing the lock that was just acquired.
*/
if (!error && fcheck(fd) != filp && flock.l_type != F_UNLCK) {
spin_lock(&current->files->file_lock);
f = fcheck(fd);
spin_unlock(&current->files->file_lock);
if (!error && f != filp && flock.l_type != F_UNLCK) {
flock.l_type = F_UNLCK;
goto again;
}
Expand Down

0 comments on commit 0b2bac2

Please sign in to comment.