fs: consistently deref the files table with rcu_dereference_raw()
Subsystems (2)
Sasha Levin
maintainerPATCH 1/2 fs: consistently deref the files table with rcu_dereference_raw()
From: Mateusz Guzik <[email protected]>
[ Upstream commit f381640e1bd4f2de7ccafbfe8703d33c3718aad9 ]
... except when the table is known to be only used by one thread.
A file pointer can get installed at any moment despite the ->file_lock being held since the following:8a81252b774b53e6 ("fs/file.c: don't acquire files->file_lock in fd_install()")
Accesses subject to such a race can in principle suffer load tearing.
While here redo the comment in dup_fd -- it only covered a race against files showing up, still assuming fd_install() takes the lock.
--- fs/file.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-)
fs/file.c +17 -9
@@ -418,17 +418,25 @@ struct files_struct *dup_fd(struct files_struct *oldf, struct fd_range *punch_ho old_fds = old_fdt->fd; new_fds = new_fdt->fd; +/* +* We may be racing against fd allocation from other threads using this +* files_struct, despite holding ->file_lock. +* +* alloc_fd() might have already claimed a slot, while fd_install() +* did not populate it yet. Note the latter operates locklessly, so +* the file can show up as we are walking the array below. +* +* At the same time we know no files will disappear as all other +* operations take the lock. +* +* Instead of trying to placate userspace racing with itself, we +* ref the file if we see it and mark the fd slot as unused otherwise. +*/ for (i = open_files; i !=0 ; i--) { -struct file *f = *old_fds++; +struct file *f =rcu_dereference_raw (*old_fds++);if (f) {get_file (f); }else { -/* -* The fd may be claimed in the fd bitmap but not yet -* instantiated in the files array if a sibling thread -* is partway through open(). So make sure that this -* fd is available to the new process. -*/ __clear_open_fd (open_files - i, new_fdt); }rcu_assign_pointer (*new_fds++, f);
@@ -679,7 +687,7 @@ struct file *file_close_fd_locked(struct files_struct *files, unsigned fd)return NULL ; fd =array_index_nospec (fd, fdt->max_fds); - file = fdt->fd[fd]; + file =rcu_dereference_raw (fdt->fd[fd]);if (file) {rcu_assign_pointer (fdt->fd[fd],NULL );__put_unused_fd (files, fd);
@@ -1237,7 +1245,7 @@ __releases(&files->file_lock)*/ fdt =files_fdtable (files); fd =array_index_nospec (fd, fdt->max_fds); - tofree = fdt->fd[fd]; + tofree =rcu_dereference_raw (fdt->fd[fd]);if (!tofree &&fd_is_open (fd, fdt)) goto Ebusy;get_file (file);
Signature · 1 line
2.39.5
Sasha Levin
maintainerPATCH 2/2 umount: Allow superblock owners to force umount
From: Trond Myklebust <[email protected]>
[ Upstream commit e1ff7aa34dec7e650159fd7ca8ec6af7cc428d9f ]
Loosen the permission check on forced umount to allow users holding CAP_SYS_ADMIN privileges in namespaces that are privileged with respect to the userns that originally mounted the filesystem.
--- fs/namespace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/namespace.c +2 -1
@@ -2026,6 +2026,7 @@ static void warn_mandlock(void)static int can_umount (const struct path *path,int flags) {struct mount *mnt =real_mount (path->mnt); +struct super_block *sb = path->dentry->d_sb;if (!may_mount ())return -EPERM ;
@@ -2035,7 +2036,7 @@ static int can_umount(const struct path *path, int flags)return -EINVAL ;if (mnt->mnt.mnt_flags &MNT_LOCKED )/* Check optimistically */ return -EINVAL ; -if (flags &MNT_FORCE && !capable (CAP_SYS_ADMIN )) +if (flags &MNT_FORCE && !ns_capable (sb->s_user_ns,CAP_SYS_ADMIN ))return -EPERM ;return 0 ; }
Signature · 1 line
2.39.5