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:
commit
76351effa5
11 changed files with 67 additions and 23 deletions
16
fs/fhandle.c
16
fs/fhandle.c
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,6 +1974,9 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,
|
|||
|
||||
inode = fuse_ilookup(fc, nodeid, &fm);
|
||||
if (inode) {
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
err = -EINVAL;
|
||||
else
|
||||
err = fuse_retrieve(fm, inode, &outarg);
|
||||
iput(inode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
10
fs/mount.h
10
fs/mount.h
|
|
@ -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 */
|
||||
|
|
|
|||
10
fs/namei.c
10
fs/namei.c
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue