file: fix close_range() for unshare+cloexec
syzbot reported a bug when putting the last reference to a tasks file descriptor table. Debugging this showed we didn't recalculate the current maximum fd number for CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC after we unshared the file descriptors table. So max_fd could exceed the current fdtable maximum causing us to set excessive bits. As a concrete example, let's say the user requested everything from fd 4 to ~0UL to be closed and their current fdtable size is 256 with their highest open fd being 4. With CLOSE_RANGE_UNSHARE the caller will end up with a new fdtable which has room for 64 file descriptors since that is the lowest fdtable size we accept. But now max_fd will still point to 255 and needs to be adjusted. Fix this by retrieving the correct maximum fd value in __range_cloexec(). Reported-by: syzbot+283ce5a46486d6acdbaf@syzkaller.appspotmail.com Fixes:582f1fb6b7
("fs, close_range: add flag CLOSE_RANGE_CLOEXEC") Fixes:fec8a6a691
("close_range: unshare all fds for CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC") Cc: Christoph Hellwig <hch@lst.de> Cc: Giuseppe Scrivano <gscrivan@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: linux-fsdevel@vger.kernel.org Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
This commit is contained in:
parent
0d02ec6b31
commit
9b5b872215
21
fs/file.c
21
fs/file.c
|
@ -629,17 +629,30 @@ int close_fd(unsigned fd)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(close_fd); /* for ksys_close() */
|
EXPORT_SYMBOL(close_fd); /* for ksys_close() */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* last_fd - return last valid index into fd table
|
||||||
|
* @cur_fds: files struct
|
||||||
|
*
|
||||||
|
* Context: Either rcu read lock or files_lock must be held.
|
||||||
|
*
|
||||||
|
* Returns: Last valid index into fdtable.
|
||||||
|
*/
|
||||||
|
static inline unsigned last_fd(struct fdtable *fdt)
|
||||||
|
{
|
||||||
|
return fdt->max_fds - 1;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void __range_cloexec(struct files_struct *cur_fds,
|
static inline void __range_cloexec(struct files_struct *cur_fds,
|
||||||
unsigned int fd, unsigned int max_fd)
|
unsigned int fd, unsigned int max_fd)
|
||||||
{
|
{
|
||||||
struct fdtable *fdt;
|
struct fdtable *fdt;
|
||||||
|
|
||||||
if (fd > max_fd)
|
/* make sure we're using the correct maximum value */
|
||||||
return;
|
|
||||||
|
|
||||||
spin_lock(&cur_fds->file_lock);
|
spin_lock(&cur_fds->file_lock);
|
||||||
fdt = files_fdtable(cur_fds);
|
fdt = files_fdtable(cur_fds);
|
||||||
bitmap_set(fdt->close_on_exec, fd, max_fd - fd + 1);
|
max_fd = min(last_fd(fdt), max_fd);
|
||||||
|
if (fd <= max_fd)
|
||||||
|
bitmap_set(fdt->close_on_exec, fd, max_fd - fd + 1);
|
||||||
spin_unlock(&cur_fds->file_lock);
|
spin_unlock(&cur_fds->file_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue