Skip to content

Commit 0e50322

Browse files
bsach64brauner
authored andcommitted
statmount: accept fd as a parameter
Extend `struct mnt_id_req` to take in a fd and introduce STATMOUNT_BY_FD flag. When a valid fd is provided and STATMOUNT_BY_FD is set, statmount will return mountinfo about the mount the fd is on. This even works for "unmounted" mounts (mounts that have been umounted using umount2(mnt, MNT_DETACH)), if you have access to a file descriptor on that mount. These "umounted" mounts will have no mountpoint and no valid mount namespace. Hence, we unset the STATMOUNT_MNT_POINT and STATMOUNT_MNT_NS_ID in statmount.mask for "unmounted" mounts. In case of STATMOUNT_BY_FD, given that we already have access to an fd on the mount, accessing mount information without a capability check seems fine because of the following reasons: - All fs related information is available via fstatfs() without any capability check. - Mount information is also available via /proc/pid/mountinfo (without any capability check). - Given that we have access to a fd on the mount which tells us that we had access to the mount at some point (or someone that had access gave us the fd). So, we should be able to access mount info. Co-developed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com> Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com> Signed-off-by: Bhavik Sachdev <b.sachdev1904@gmail.com> Link: https://patch.msgid.link/20251129091455.757724-3-b.sachdev1904@gmail.com Acked-by: Andrei Vagin <avagin@gmail.com> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent fccbe38 commit 0e50322

2 files changed

Lines changed: 76 additions & 36 deletions

File tree

fs/namespace.c

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5547,31 +5547,49 @@ static int grab_requested_root(struct mnt_namespace *ns, struct path *root)
55475547

55485548
/* locks: namespace_shared */
55495549
static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
5550-
struct mnt_namespace *ns)
5550+
struct file *mnt_file, struct mnt_namespace *ns)
55515551
{
5552-
struct mount *m;
55535552
int err;
55545553

5555-
/* Has the namespace already been emptied? */
5556-
if (mnt_ns_id && mnt_ns_empty(ns))
5557-
return -ENOENT;
5554+
if (mnt_file) {
5555+
WARN_ON_ONCE(ns != NULL);
55585556

5559-
s->mnt = lookup_mnt_in_ns(mnt_id, ns);
5560-
if (!s->mnt)
5561-
return -ENOENT;
5557+
s->mnt = mnt_file->f_path.mnt;
5558+
ns = real_mount(s->mnt)->mnt_ns;
5559+
if (!ns)
5560+
/*
5561+
* We can't set mount point and mnt_ns_id since we don't have a
5562+
* ns for the mount. This can happen if the mount is unmounted
5563+
* with MNT_DETACH.
5564+
*/
5565+
s->mask &= ~(STATMOUNT_MNT_POINT | STATMOUNT_MNT_NS_ID);
5566+
} else {
5567+
/* Has the namespace already been emptied? */
5568+
if (mnt_ns_id && mnt_ns_empty(ns))
5569+
return -ENOENT;
55625570

5563-
err = grab_requested_root(ns, &s->root);
5564-
if (err)
5565-
return err;
5571+
s->mnt = lookup_mnt_in_ns(mnt_id, ns);
5572+
if (!s->mnt)
5573+
return -ENOENT;
5574+
}
55665575

5567-
/*
5568-
* Don't trigger audit denials. We just want to determine what
5569-
* mounts to show users.
5570-
*/
5571-
m = real_mount(s->mnt);
5572-
if (!is_path_reachable(m, m->mnt.mnt_root, &s->root) &&
5573-
!ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
5574-
return -EPERM;
5576+
if (ns) {
5577+
err = grab_requested_root(ns, &s->root);
5578+
if (err)
5579+
return err;
5580+
5581+
if (!mnt_file) {
5582+
struct mount *m;
5583+
/*
5584+
* Don't trigger audit denials. We just want to determine what
5585+
* mounts to show users.
5586+
*/
5587+
m = real_mount(s->mnt);
5588+
if (!is_path_reachable(m, m->mnt.mnt_root, &s->root) &&
5589+
!ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
5590+
return -EPERM;
5591+
}
5592+
}
55755593

55765594
err = security_sb_statfs(s->mnt->mnt_root);
55775595
if (err)
@@ -5693,7 +5711,7 @@ static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
56935711
}
56945712

56955713
static int copy_mnt_id_req(const struct mnt_id_req __user *req,
5696-
struct mnt_id_req *kreq)
5714+
struct mnt_id_req *kreq, unsigned int flags)
56975715
{
56985716
int ret;
56995717
size_t usize;
@@ -5711,11 +5729,17 @@ static int copy_mnt_id_req(const struct mnt_id_req __user *req,
57115729
ret = copy_struct_from_user(kreq, sizeof(*kreq), req, usize);
57125730
if (ret)
57135731
return ret;
5714-
if (kreq->mnt_ns_fd != 0 && kreq->mnt_ns_id)
5715-
return -EINVAL;
5716-
/* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */
5717-
if (kreq->mnt_id <= MNT_UNIQUE_ID_OFFSET)
5718-
return -EINVAL;
5732+
5733+
if (flags & STATMOUNT_BY_FD) {
5734+
if (kreq->mnt_id || kreq->mnt_ns_id)
5735+
return -EINVAL;
5736+
} else {
5737+
if (kreq->mnt_ns_fd != 0 && kreq->mnt_ns_id)
5738+
return -EINVAL;
5739+
/* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */
5740+
if (kreq->mnt_id <= MNT_UNIQUE_ID_OFFSET)
5741+
return -EINVAL;
5742+
}
57195743
return 0;
57205744
}
57215745

@@ -5762,25 +5786,33 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req,
57625786
{
57635787
struct mnt_namespace *ns __free(mnt_ns_release) = NULL;
57645788
struct kstatmount *ks __free(kfree) = NULL;
5789+
struct file *mnt_file __free(fput) = NULL;
57655790
struct mnt_id_req kreq;
57665791
/* We currently support retrieval of 3 strings. */
57675792
size_t seq_size = 3 * PATH_MAX;
57685793
int ret;
57695794

5770-
if (flags)
5795+
if (flags & ~STATMOUNT_BY_FD)
57715796
return -EINVAL;
57725797

5773-
ret = copy_mnt_id_req(req, &kreq);
5798+
ret = copy_mnt_id_req(req, &kreq, flags);
57745799
if (ret)
57755800
return ret;
57765801

5777-
ns = grab_requested_mnt_ns(&kreq);
5778-
if (IS_ERR(ns))
5779-
return PTR_ERR(ns);
5802+
if (flags & STATMOUNT_BY_FD) {
5803+
mnt_file = fget_raw(kreq.mnt_fd);
5804+
if (!mnt_file)
5805+
return -EBADF;
5806+
/* do_statmount sets ns in case of STATMOUNT_BY_FD */
5807+
} else {
5808+
ns = grab_requested_mnt_ns(&kreq);
5809+
if (IS_ERR(ns))
5810+
return PTR_ERR(ns);
57805811

5781-
if (kreq.mnt_ns_id && (ns != current->nsproxy->mnt_ns) &&
5782-
!ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
5783-
return -EPERM;
5812+
if (kreq.mnt_ns_id && (ns != current->nsproxy->mnt_ns) &&
5813+
!ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
5814+
return -EPERM;
5815+
}
57845816

57855817
ks = kmalloc(sizeof(*ks), GFP_KERNEL_ACCOUNT);
57865818
if (!ks)
@@ -5792,7 +5824,7 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req,
57925824
return ret;
57935825

57945826
scoped_guard(namespace_shared)
5795-
ret = do_statmount(ks, kreq.mnt_id, kreq.mnt_ns_id, ns);
5827+
ret = do_statmount(ks, kreq.mnt_id, kreq.mnt_ns_id, mnt_file, ns);
57965828

57975829
if (!ret)
57985830
ret = copy_statmount_to_user(ks);
@@ -5932,7 +5964,7 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
59325964
if (!access_ok(mnt_ids, nr_mnt_ids * sizeof(*mnt_ids)))
59335965
return -EFAULT;
59345966

5935-
ret = copy_mnt_id_req(req, &kreq);
5967+
ret = copy_mnt_id_req(req, &kreq, 0);
59365968
if (ret)
59375969
return ret;
59385970

include/uapi/linux/mount.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,10 @@ struct statmount {
197197
*/
198198
struct mnt_id_req {
199199
__u32 size;
200-
__u32 mnt_ns_fd;
200+
union {
201+
__u32 mnt_ns_fd;
202+
__u32 mnt_fd;
203+
};
201204
__u64 mnt_id;
202205
__u64 param;
203206
__u64 mnt_ns_id;
@@ -232,4 +235,9 @@ struct mnt_id_req {
232235
#define LSMT_ROOT 0xffffffffffffffff /* root mount */
233236
#define LISTMOUNT_REVERSE (1 << 0) /* List later mounts first */
234237

238+
/*
239+
* @flag bits for statmount(2)
240+
*/
241+
#define STATMOUNT_BY_FD 0x00000001U /* want mountinfo for given fd */
242+
235243
#endif /* _UAPI_LINUX_MOUNT_H */

0 commit comments

Comments
 (0)