Skip to content

Commit ba5afb9

Browse files
committed
fs: rework listmount() implementation
Linus pointed out that there's error handling and naming issues in the that we should rewrite: * Perform the access checks for the buffer before actually doing any work instead of doing it during the iteration. * Rename the arguments to listmount() and do_listmount() to clarify what the arguments are used for. * Get rid of the pointless ctr variable and overflow checking. * Get rid of the pointless speculation check. Link: https://lore.kernel.org/r/CAHk-=wjh6Cypo8WC-McXgSzCaou3UXccxB+7PVeSuGR8AjCphg@mail.gmail.com Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 7ea26f9 commit ba5afb9

2 files changed

Lines changed: 29 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/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)