Skip to content

Commit 3cad1bc

Browse files
thejhbrauner
authored andcommitted
filelock: Remove locks reliably when fcntl/close race is detected
When fcntl_setlk() races with close(), it removes the created lock with do_lock_file_wait(). However, LSMs can allow the first do_lock_file_wait() that created the lock while denying the second do_lock_file_wait() that tries to remove the lock. In theory (but AFAIK not in practice), posix_lock_file() could also fail to remove a lock due to GFP_KERNEL allocation failure (when splitting a range in the middle). After the bug has been triggered, use-after-free reads will occur in lock_get_status() when userspace reads /proc/locks. This can likely be used to read arbitrary kernel memory, but can't corrupt kernel memory. This only affects systems with SELinux / Smack / AppArmor / BPF-LSM in enforcing mode and only works from some security contexts. Fix it by calling locks_remove_posix() instead, which is designed to reliably get rid of POSIX locks associated with the given file and files_struct and is also used by filp_flush(). Fixes: c293621 ("[PATCH] stale POSIX lock handling") Cc: [email protected] Link: https://bugs.chromium.org/p/project-zero/issues/detail?id=2563 Signed-off-by: Jann Horn <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent 22a40d1 commit 3cad1bc

File tree

1 file changed

+4
-5
lines changed

1 file changed

+4
-5
lines changed

fs/locks.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2448,8 +2448,9 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
24482448
error = do_lock_file_wait(filp, cmd, file_lock);
24492449

24502450
/*
2451-
* Attempt to detect a close/fcntl race and recover by releasing the
2452-
* lock that was just acquired. There is no need to do that when we're
2451+
* Detect close/fcntl races and recover by zapping all POSIX locks
2452+
* associated with this file and our files_struct, just like on
2453+
* filp_flush(). There is no need to do that when we're
24532454
* unlocking though, or for OFD locks.
24542455
*/
24552456
if (!error && file_lock->c.flc_type != F_UNLCK &&
@@ -2464,9 +2465,7 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
24642465
f = files_lookup_fd_locked(files, fd);
24652466
spin_unlock(&files->file_lock);
24662467
if (f != filp) {
2467-
file_lock->c.flc_type = F_UNLCK;
2468-
error = do_lock_file_wait(filp, cmd, file_lock);
2469-
WARN_ON_ONCE(error);
2468+
locks_remove_posix(filp, files);
24702469
error = -EBADF;
24712470
}
24722471
}

0 commit comments

Comments
 (0)