Skip to content

Commit c2459ce

Browse files
committed
Merge tag 'vfs-6.8-rc1.fixes' of gitolite.kernel.org:pub/scm/linux/kernel/git/vfs/vfs
Pull vfs fixes from Christian Brauner: "This contains two fixes for the current merge window. The listmount changes that you requested and a fix for a fsnotify performance regression: - The proposed listmount changes are currently under my authorship. I wasn't sure whether you'd wanted to be author as the patch wasn't signed off. If you do I'm happy if you just apply your own patch. I've tested the patch with my sh4 cross-build setup. And confirmed that a) the build failure with sh on current upstream is reproducible and that b) the proposed patch fixes the build failure. That should only leave the task of fixing put_user on sh. - The fsnotify regression was caused by moving one of the hooks out of the security hook in preparation for other fsnotify work. This meant that CONFIG_SECURITY would have compiled out the fsnotify hook before but didn't do so now. That lead to up to 6% performance regression in some io_uring workloads that compile all fsnotify and security checks out. Fix this by making sure that the relevant hooks are covered by the already existing CONFIG_FANOTIFY_ACCESS_PERMISSIONS where the relevant hook belongs" * tag 'vfs-6.8-rc1.fixes' of gitolite.kernel.org:pub/scm/linux/kernel/git/vfs/vfs: fs: rework listmount() implementation fsnotify: compile out fsnotify permission hooks if !FANOTIFY_ACCESS_PERMISSIONS
2 parents 7f5e47f + ba5afb9 commit c2459ce

3 files changed

Lines changed: 48 additions & 23 deletions

File tree

fs/namespace.c

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5042,13 +5042,12 @@ static struct mount *listmnt_next(struct mount *curr)
50425042
return node_to_mount(rb_next(&curr->mnt_node));
50435043
}
50445044

5045-
static ssize_t do_listmount(struct mount *first, struct path *orig, u64 mnt_id,
5046-
u64 __user *buf, size_t bufsize,
5047-
const struct path *root)
5045+
static ssize_t do_listmount(struct mount *first, struct path *orig,
5046+
u64 mnt_parent_id, u64 __user *mnt_ids,
5047+
size_t nr_mnt_ids, const struct path *root)
50485048
{
50495049
struct mount *r;
5050-
ssize_t ctr;
5051-
int err;
5050+
ssize_t ret;
50525051

50535052
/*
50545053
* Don't trigger audit denials. We just want to determine what
@@ -5058,50 +5057,57 @@ static ssize_t do_listmount(struct mount *first, struct path *orig, u64 mnt_id,
50585057
!ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN))
50595058
return -EPERM;
50605059

5061-
err = security_sb_statfs(orig->dentry);
5062-
if (err)
5063-
return err;
5060+
ret = security_sb_statfs(orig->dentry);
5061+
if (ret)
5062+
return ret;
50645063

5065-
for (ctr = 0, r = first; r && ctr < bufsize; r = listmnt_next(r)) {
5066-
if (r->mnt_id_unique == mnt_id)
5064+
for (ret = 0, r = first; r && nr_mnt_ids; r = listmnt_next(r)) {
5065+
if (r->mnt_id_unique == mnt_parent_id)
50675066
continue;
50685067
if (!is_path_reachable(r, r->mnt.mnt_root, orig))
50695068
continue;
5070-
ctr = array_index_nospec(ctr, bufsize);
5071-
if (put_user(r->mnt_id_unique, buf + ctr))
5069+
if (put_user(r->mnt_id_unique, mnt_ids))
50725070
return -EFAULT;
5073-
if (check_add_overflow(ctr, 1, &ctr))
5074-
return -ERANGE;
5071+
mnt_ids++;
5072+
nr_mnt_ids--;
5073+
ret++;
50755074
}
5076-
return ctr;
5075+
return ret;
50775076
}
50785077

5079-
SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
5080-
u64 __user *, buf, size_t, bufsize, unsigned int, flags)
5078+
SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req, u64 __user *,
5079+
mnt_ids, size_t, nr_mnt_ids, unsigned int, flags)
50815080
{
50825081
struct mnt_namespace *ns = current->nsproxy->mnt_ns;
50835082
struct mnt_id_req kreq;
50845083
struct mount *first;
50855084
struct path root, orig;
5086-
u64 mnt_id, last_mnt_id;
5085+
u64 mnt_parent_id, last_mnt_id;
5086+
const size_t maxcount = (size_t)-1 >> 3;
50875087
ssize_t ret;
50885088

50895089
if (flags)
50905090
return -EINVAL;
50915091

5092+
if (unlikely(nr_mnt_ids > maxcount))
5093+
return -EFAULT;
5094+
5095+
if (!access_ok(mnt_ids, nr_mnt_ids * sizeof(*mnt_ids)))
5096+
return -EFAULT;
5097+
50925098
ret = copy_mnt_id_req(req, &kreq);
50935099
if (ret)
50945100
return ret;
5095-
mnt_id = kreq.mnt_id;
5101+
mnt_parent_id = kreq.mnt_id;
50965102
last_mnt_id = kreq.param;
50975103

50985104
down_read(&namespace_sem);
50995105
get_fs_root(current->fs, &root);
5100-
if (mnt_id == LSMT_ROOT) {
5106+
if (mnt_parent_id == LSMT_ROOT) {
51015107
orig = root;
51025108
} else {
51035109
ret = -ENOENT;
5104-
orig.mnt = lookup_mnt_in_ns(mnt_id, ns);
5110+
orig.mnt = lookup_mnt_in_ns(mnt_parent_id, ns);
51055111
if (!orig.mnt)
51065112
goto err;
51075113
orig.dentry = orig.mnt->mnt_root;
@@ -5111,7 +5117,7 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
51115117
else
51125118
first = mnt_find_id_at(ns, last_mnt_id + 1);
51135119

5114-
ret = do_listmount(first, &orig, mnt_id, buf, bufsize, &root);
5120+
ret = do_listmount(first, &orig, mnt_parent_id, mnt_ids, nr_mnt_ids, &root);
51155121
err:
51165122
path_put(&root);
51175123
up_read(&namespace_sem);

include/linux/fsnotify.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ static inline int fsnotify_file(struct file *file, __u32 mask)
100100
return fsnotify_parent(path->dentry, mask, path, FSNOTIFY_EVENT_PATH);
101101
}
102102

103+
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
103104
/*
104105
* fsnotify_file_area_perm - permission hook before access to file range
105106
*/
@@ -145,6 +146,24 @@ static inline int fsnotify_open_perm(struct file *file)
145146
return fsnotify_file(file, FS_OPEN_PERM);
146147
}
147148

149+
#else
150+
static inline int fsnotify_file_area_perm(struct file *file, int perm_mask,
151+
const loff_t *ppos, size_t count)
152+
{
153+
return 0;
154+
}
155+
156+
static inline int fsnotify_file_perm(struct file *file, int perm_mask)
157+
{
158+
return 0;
159+
}
160+
161+
static inline int fsnotify_open_perm(struct file *file)
162+
{
163+
return 0;
164+
}
165+
#endif
166+
148167
/*
149168
* fsnotify_link_count - inode's link count changed
150169
*/

include/linux/syscalls.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ asmlinkage long sys_statmount(const struct mnt_id_req __user *req,
414414
struct statmount __user *buf, size_t bufsize,
415415
unsigned int flags);
416416
asmlinkage long sys_listmount(const struct mnt_id_req __user *req,
417-
u64 __user *buf, size_t bufsize,
417+
u64 __user *mnt_ids, size_t nr_mnt_ids,
418418
unsigned int flags);
419419
asmlinkage long sys_truncate(const char __user *path, long length);
420420
asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length);

0 commit comments

Comments
 (0)