Skip to content

Commit 0a3deb1

Browse files
committed
fs: Allow listmount() in foreign mount namespace
Expand struct mnt_id_req to add an optional mnt_ns_id field. When this field is populated, listmount() will be performed on the specified mount namespace, provided the currently application has CAP_SYS_ADMIN in its user namespace and the mount namespace is a child of the current namespace. Co-developed-by: Josef Bacik <[email protected]> Signed-off-by: Josef Bacik <[email protected]> Link: https://lore.kernel.org/r/49930bdce29a8367a213eb14c1e68e7e49284f86.1719243756.git.josef@toxicpanda.com Signed-off-by: Christian Brauner <[email protected]>
1 parent 09b3129 commit 0a3deb1

File tree

2 files changed

+72
-18
lines changed

2 files changed

+72
-18
lines changed

fs/namespace.c

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5122,7 +5122,7 @@ static int copy_mnt_id_req(const struct mnt_id_req __user *req,
51225122
int ret;
51235123
size_t usize;
51245124

5125-
BUILD_BUG_ON(sizeof(struct mnt_id_req) != MNT_ID_REQ_SIZE_VER0);
5125+
BUILD_BUG_ON(sizeof(struct mnt_id_req) != MNT_ID_REQ_SIZE_VER1);
51265126

51275127
ret = get_user(usize, &req->size);
51285128
if (ret)
@@ -5140,6 +5140,58 @@ static int copy_mnt_id_req(const struct mnt_id_req __user *req,
51405140
return 0;
51415141
}
51425142

5143+
static struct mount *listmnt_next(struct mount *curr, bool reverse)
5144+
{
5145+
struct rb_node *node;
5146+
5147+
if (reverse)
5148+
node = rb_prev(&curr->mnt_node);
5149+
else
5150+
node = rb_next(&curr->mnt_node);
5151+
5152+
return node_to_mount(node);
5153+
}
5154+
5155+
static int grab_requested_root(struct mnt_namespace *ns, struct path *root)
5156+
{
5157+
struct mount *first;
5158+
5159+
rwsem_assert_held(&namespace_sem);
5160+
5161+
/* We're looking at our own ns, just use get_fs_root. */
5162+
if (ns == current->nsproxy->mnt_ns) {
5163+
get_fs_root(current->fs, root);
5164+
return 0;
5165+
}
5166+
5167+
/*
5168+
* We have to find the first mount in our ns and use that, however it
5169+
* may not exist, so handle that properly.
5170+
*/
5171+
if (RB_EMPTY_ROOT(&ns->mounts))
5172+
return -ENOENT;
5173+
5174+
first = listmnt_next(ns->root, false);
5175+
if (!first)
5176+
return -ENOENT;
5177+
root->mnt = mntget(&first->mnt);
5178+
root->dentry = dget(root->mnt->mnt_root);
5179+
return 0;
5180+
}
5181+
5182+
/*
5183+
* If the user requested a specific mount namespace id, look that up and return
5184+
* that, or if not simply grab a passive reference on our mount namespace and
5185+
* return that.
5186+
*/
5187+
static struct mnt_namespace *grab_requested_mnt_ns(u64 mnt_ns_id)
5188+
{
5189+
if (mnt_ns_id)
5190+
return lookup_mnt_ns(mnt_ns_id);
5191+
refcount_inc(&current->nsproxy->mnt_ns->passive);
5192+
return current->nsproxy->mnt_ns;
5193+
}
5194+
51435195
SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req,
51445196
struct statmount __user *, buf, size_t, bufsize,
51455197
unsigned int, flags)
@@ -5185,30 +5237,21 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req,
51855237
return ret;
51865238
}
51875239

5188-
static struct mount *listmnt_next(struct mount *curr, bool reverse)
5189-
{
5190-
struct rb_node *node;
5191-
5192-
if (reverse)
5193-
node = rb_prev(&curr->mnt_node);
5194-
else
5195-
node = rb_next(&curr->mnt_node);
5196-
5197-
return node_to_mount(node);
5198-
}
5199-
5200-
static ssize_t do_listmount(u64 mnt_parent_id, u64 last_mnt_id, u64 *mnt_ids,
5201-
size_t nr_mnt_ids, bool reverse)
5240+
static ssize_t do_listmount(struct mnt_namespace *ns, u64 mnt_parent_id,
5241+
u64 last_mnt_id, u64 *mnt_ids, size_t nr_mnt_ids,
5242+
bool reverse)
52025243
{
52035244
struct path root __free(path_put) = {};
5204-
struct mnt_namespace *ns = current->nsproxy->mnt_ns;
52055245
struct path orig;
52065246
struct mount *r, *first;
52075247
ssize_t ret;
52085248

52095249
rwsem_assert_held(&namespace_sem);
52105250

5211-
get_fs_root(current->fs, &root);
5251+
ret = grab_requested_root(ns, &root);
5252+
if (ret)
5253+
return ret;
5254+
52125255
if (mnt_parent_id == LSMT_ROOT) {
52135256
orig = root;
52145257
} else {
@@ -5260,6 +5303,7 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
52605303
{
52615304
u64 *kmnt_ids __free(kvfree) = NULL;
52625305
const size_t maxcount = 1000000;
5306+
struct mnt_namespace *ns __free(mnt_ns_release) = NULL;
52635307
struct mnt_id_req kreq;
52645308
ssize_t ret;
52655309

@@ -5286,8 +5330,16 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
52865330
if (!kmnt_ids)
52875331
return -ENOMEM;
52885332

5333+
ns = grab_requested_mnt_ns(kreq.mnt_ns_id);
5334+
if (!ns)
5335+
return -ENOENT;
5336+
5337+
if (kreq.mnt_ns_id && (ns != current->nsproxy->mnt_ns) &&
5338+
!ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
5339+
return -ENOENT;
5340+
52895341
scoped_guard(rwsem_read, &namespace_sem)
5290-
ret = do_listmount(kreq.mnt_id, kreq.param, kmnt_ids,
5342+
ret = do_listmount(ns, kreq.mnt_id, kreq.param, kmnt_ids,
52915343
nr_mnt_ids, (flags & LISTMOUNT_REVERSE));
52925344

52935345
if (copy_to_user(mnt_ids, kmnt_ids, ret * sizeof(*mnt_ids)))

include/uapi/linux/mount.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,12 @@ struct mnt_id_req {
189189
__u32 spare;
190190
__u64 mnt_id;
191191
__u64 param;
192+
__u64 mnt_ns_id;
192193
};
193194

194195
/* List of all mnt_id_req versions. */
195196
#define MNT_ID_REQ_SIZE_VER0 24 /* sizeof first published struct */
197+
#define MNT_ID_REQ_SIZE_VER1 32 /* sizeof second published struct */
196198

197199
/*
198200
* @mask bits for statmount(2)

0 commit comments

Comments
 (0)