vfs-7.1-rc7.fixes

Please consider pulling these changes from the signed vfs-7.1-rc7.fixes tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaiQb2wAKCRCRxhvAZXjc
 ol4fAP46Ta2gcT8uzJRHaXo3HMWJYY02dK1pXQ4zZDdiQMMSTgEAuaj/mOvx0VeX
 prloV/t7TBri6GAiWeE7Wu54d9rJZQ4=
 =clMX
 -----END PGP SIGNATURE-----

Merge tag 'vfs-7.1-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs fixes from Christian Brauner:

 - Fix error handling in ovl_cache_get()

 - Tighten access checks for exited tasks in pidfd_getfd()

 - Fix selftests leak in __wait_for_test()

 - Limit FUSE_NOTIFY_RETRIEVE to uptodate folios

 - Reject fuse_notify() pagecache ops on directories

 - Clear JOBCTL_PENDING_MASK for caller in zap_other_threads()

 - Fix failure to unlock in nfsd4_create_file()

 - Fix pointer arithmetic in qnx6 directory iteration

 - Fix UAF due to unlocked ->mnt_ns read in may_decode_fh()

 - Avoid potential null folio->mapping deref during iomap error
   reporting

* tag 'vfs-7.1-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  iomap: avoid potential null folio->mapping deref during error reporting
  fhandle: fix UAF due to unlocked ->mnt_ns read in may_decode_fh()
  fs/qnx6: fix pointer arithmetic in directory iteration
  VFS: fix possible failure to unlock in nfsd4_create_file()
  signal: clear JOBCTL_PENDING_MASK for caller in zap_other_threads()
  fuse: reject fuse_notify() pagecache ops on directories
  fuse: limit FUSE_NOTIFY_RETRIEVE to uptodate folios
  selftests: harness: fix pidfd leak in __wait_for_test
  pidfd: refuse access to tasks that have started exiting harder
  ovl: keep err zero after successful ovl_cache_get()
This commit is contained in:
Linus Torvalds 2026-06-06 07:28:59 -07:00
commit 76351effa5
11 changed files with 67 additions and 23 deletions

View file

@ -285,6 +285,19 @@ static int do_handle_to_path(struct file_handle *handle, struct path *path,
return 0;
}
static bool capable_wrt_mount(struct mount *mount)
{
struct mnt_namespace *mnt_ns;
/*
* For ->mnt_ns access.
* The following READ_ONCE() is semantically rcu_dereference().
*/
guard(rcu)();
mnt_ns = READ_ONCE(mount->mnt_ns);
return ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN);
}
static inline int may_decode_fh(struct handle_to_path_ctx *ctx,
unsigned int o_flags)
{
@ -320,8 +333,7 @@ static inline int may_decode_fh(struct handle_to_path_ctx *ctx,
if (ns_capable(root->mnt->mnt_sb->s_user_ns, CAP_SYS_ADMIN))
ctx->flags = HANDLE_CHECK_PERMS;
else if (is_mounted(root->mnt) &&
ns_capable(real_mount(root->mnt)->mnt_ns->user_ns,
CAP_SYS_ADMIN) &&
capable_wrt_mount(real_mount(root->mnt)) &&
!has_locked_children(real_mount(root->mnt), root->dentry))
ctx->flags = HANDLE_CHECK_PERMS | HANDLE_CHECK_SUBTREE;
else

View file

@ -1793,6 +1793,10 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
inode = fuse_ilookup(fc, nodeid, NULL);
if (!inode)
goto out_up_killsb;
if (!S_ISREG(inode->i_mode)) {
err = -EINVAL;
goto out_iput;
}
mapping = inode->i_mapping;
file_size = i_size_read(inode);
@ -1912,6 +1916,10 @@ static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
folio = filemap_get_folio(mapping, index);
if (IS_ERR(folio))
break;
if (!folio_test_uptodate(folio)) {
folio_put(folio);
break;
}
folio_offset = offset_in_folio(folio, pos);
nr_bytes = min(folio_size(folio) - folio_offset, num);
@ -1966,7 +1974,10 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,
inode = fuse_ilookup(fc, nodeid, &fm);
if (inode) {
err = fuse_retrieve(fm, inode, &outarg);
if (!S_ISREG(inode->i_mode))
err = -EINVAL;
else
err = fuse_retrieve(fm, inode, &outarg);
iput(inode);
}
up_read(&fc->killsb);

View file

@ -400,6 +400,11 @@ void iomap_finish_folio_read(struct folio *folio, size_t off, size_t len,
bool uptodate = !error;
bool finished = true;
if (error)
fserror_report_io(folio->mapping->host, FSERR_BUFFERED_READ,
folio_pos(folio) + off, len, error,
GFP_ATOMIC);
if (ifs) {
unsigned long flags;
@ -411,11 +416,6 @@ void iomap_finish_folio_read(struct folio *folio, size_t off, size_t len,
spin_unlock_irqrestore(&ifs->state_lock, flags);
}
if (error)
fserror_report_io(folio->mapping->host, FSERR_BUFFERED_READ,
folio_pos(folio) + off, len, error,
GFP_ATOMIC);
if (finished)
folio_end_read(folio, uptodate);
}

View file

@ -71,7 +71,15 @@ struct mount {
struct hlist_head mnt_slave_list;/* list of slave mounts */
struct hlist_node mnt_slave; /* slave list entry */
struct mount *mnt_master; /* slave is on master->mnt_slave_list */
struct mnt_namespace *mnt_ns; /* containing namespace */
/*
* Containing namespace (active or deactivating, non-refcounted).
* Normally protected by namespace_sem.
* Can also be accessed locklessly under RCU. RCU readers can't rely on
* the namespace still being active, but implicitly hold a passive
* reference (because an RCU delay happens between a namespace being
* deactivated and the corresponding passive refcount drop).
*/
struct mnt_namespace *mnt_ns;
struct mountpoint *mnt_mp; /* where is it mounted */
union {
struct hlist_node mnt_mp_list; /* list mounts with the same mountpoint */

View file

@ -5024,6 +5024,7 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode,
{
struct file *file __free(fput) = NULL;
struct dentry *dentry = path->dentry;
struct dentry *orig_dentry = dentry;
struct dentry *dir = dentry->d_parent;
struct inode *dir_inode = d_inode(dir);
struct mnt_idmap *idmap;
@ -5043,9 +5044,18 @@ struct file *dentry_create(struct path *path, int flags, umode_t mode,
if (create_error)
flags &= ~O_CREAT;
/* atomic_open will dput(dentry) on error */
dget(orig_dentry);
dentry = atomic_open(path, dentry, file, flags, mode);
error = PTR_ERR_OR_ZERO(dentry);
if (IS_ERR(dentry))
/* keep the original */
dentry = orig_dentry;
else
/* Drop the extra reference */
dput(orig_dentry);
if (unlikely(create_error) && error == -ENOENT)
error = create_error;

View file

@ -1079,7 +1079,7 @@ static void mnt_add_to_ns(struct mnt_namespace *ns, struct mount *mnt)
bool mnt_first_node = true, mnt_last_node = true;
WARN_ON(mnt_ns_attached(mnt));
mnt->mnt_ns = ns;
WRITE_ONCE(mnt->mnt_ns, ns);
while (*link) {
parent = *link;
if (mnt->mnt_id_unique < node_to_mount(parent)->mnt_id_unique) {
@ -1434,7 +1434,7 @@ EXPORT_SYMBOL(mntget);
void mnt_make_shortterm(struct vfsmount *mnt)
{
if (mnt)
real_mount(mnt)->mnt_ns = NULL;
WRITE_ONCE(real_mount(mnt)->mnt_ns, NULL);
}
/**
@ -1806,7 +1806,7 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how)
ns->nr_mounts--;
__touch_mnt_namespace(ns);
}
p->mnt_ns = NULL;
WRITE_ONCE(p->mnt_ns, NULL);
if (how & UMOUNT_SYNC)
p->mnt.mnt_flags |= MNT_SYNC_UMOUNT;

View file

@ -838,15 +838,14 @@ static int ovl_iterate_merged(struct file *file, struct dir_context *ctx)
struct ovl_dir_file *od = file->private_data;
struct dentry *dentry = file->f_path.dentry;
struct ovl_cache_entry *p;
int err = 0;
int err;
if (!od->cache) {
struct ovl_dir_cache *cache;
cache = ovl_cache_get(dentry);
err = PTR_ERR(cache);
if (IS_ERR(cache))
return err;
return PTR_ERR(cache);
od->cache = cache;
ovl_seek_cursor(od, ctx->pos);
@ -869,7 +868,7 @@ static int ovl_iterate_merged(struct file *file, struct dir_context *ctx)
od->cursor = p->l_node.next;
ctx->pos++;
}
return err;
return 0;
}
static bool ovl_need_adjust_d_ino(struct file *file)

View file

@ -132,16 +132,16 @@ static int qnx6_readdir(struct file *file, struct dir_context *ctx)
struct qnx6_dir_entry *de;
struct folio *folio;
char *kaddr = qnx6_get_folio(inode, n, &folio);
char *limit;
struct qnx6_dir_entry *limit;
if (IS_ERR(kaddr)) {
pr_err("%s(): read failed\n", __func__);
ctx->pos = (n + 1) << PAGE_SHIFT;
return PTR_ERR(kaddr);
}
de = (struct qnx6_dir_entry *)(kaddr + offset);
limit = kaddr + last_entry(inode, n);
for (; (char *)de < limit; de++, ctx->pos += QNX6_DIR_ENTRY_SIZE) {
de = (struct qnx6_dir_entry *)kaddr + offset;
limit = (struct qnx6_dir_entry *)kaddr + last_entry(inode, n);
for (; de < limit; de++, ctx->pos += QNX6_DIR_ENTRY_SIZE) {
int size = de->de_size;
u32 no_inode = fs32_to_cpu(sbi, de->de_inode);

View file

@ -885,10 +885,12 @@ static struct file *__pidfd_fget(struct task_struct *task, int fd)
if (ret)
return ERR_PTR(ret);
if (ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS))
file = fget_task(task, fd);
else
if (!ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS))
file = ERR_PTR(-EPERM);
else if (task->flags & PF_EXITING)
file = ERR_PTR(-ESRCH);
else
file = fget_task(task, fd);
up_read(&task->signal->exec_update_lock);

View file

@ -1338,6 +1338,7 @@ int zap_other_threads(struct task_struct *p)
int count = 0;
p->signal->group_stop_count = 0;
task_clear_jobctl_pending(p, JOBCTL_PENDING_MASK);
for_other_threads(p, t) {
task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK);

View file

@ -996,6 +996,7 @@ static void __wait_for_test(struct __test_metadata *t)
poll_child.fd = childfd;
poll_child.events = POLLIN;
ret = poll(&poll_child, 1, t->timeout * 1000);
close(childfd);
if (ret == -1) {
t->exit_code = KSFT_FAIL;
fprintf(TH_LOG_STREAM,