From d6da19c9cace63290ccfccb1fc35151ffefc0bec Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 16 Aug 2022 17:53:17 +0300 Subject: locks: fix TOCTOU race when granting write lease Thread A trying to acquire a write lease checks the value of i_readcount and i_writecount in check_conflicting_open() to verify that its own fd is the only fd referencing the file. Thread B trying to open the file for read will call break_lease() in do_dentry_open() before incrementing i_readcount, which leaves a small window where thread A can acquire the write lease and then thread B completes the open of the file for read without breaking the write lease that was acquired by thread A. Fix this race by incrementing i_readcount before checking for existing leases, same as the case with i_writecount. Use a helper put_file_access() to decrement i_readcount or i_writecount in do_dentry_open() and __fput(). Fixes: 387e3746d01c ("locks: eliminate false positive conflicts for write lease") Reviewed-by: Jeff Layton Signed-off-by: Amir Goldstein Signed-off-by: Al Viro --- fs/internal.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'fs/internal.h') diff --git a/fs/internal.h b/fs/internal.h index 87e96b9024ce..31861e6c3eff 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -101,6 +101,16 @@ extern void chroot_fs_refs(const struct path *, const struct path *); extern struct file *alloc_empty_file(int, const struct cred *); extern struct file *alloc_empty_file_noaccount(int, const struct cred *); +static inline void put_file_access(struct file *file) +{ + if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) { + i_readcount_dec(file->f_inode); + } else if (file->f_mode & FMODE_WRITER) { + put_write_access(file->f_inode); + __mnt_drop_write(file->f_path.mnt); + } +} + /* * super.c */ -- cgit