Skip to content

Commit c1f86d0

Browse files
committed
listmount: don't call path_put() under namespace semaphore
Massage listmount() and make sure we don't call path_put() under the namespace semaphore. If we put the last reference we're fscked. Fixes: b4c2bea ("add listmount(2) syscall") Cc: stable@vger.kernel.org # v6.8+ Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent e8c84e2 commit c1f86d0

1 file changed

Lines changed: 59 additions & 28 deletions

File tree

fs/namespace.c

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5963,23 +5963,34 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req,
59635963
return ret;
59645964
}
59655965

5966-
static ssize_t do_listmount(struct mnt_namespace *ns, u64 mnt_parent_id,
5967-
u64 last_mnt_id, u64 *mnt_ids, size_t nr_mnt_ids,
5968-
bool reverse)
5966+
struct klistmount {
5967+
u64 last_mnt_id;
5968+
u64 mnt_parent_id;
5969+
u64 *kmnt_ids;
5970+
u32 nr_mnt_ids;
5971+
struct mnt_namespace *ns;
5972+
struct path root;
5973+
};
5974+
5975+
static ssize_t do_listmount(struct klistmount *kls, bool reverse)
59695976
{
5970-
struct path root __free(path_put) = {};
5977+
struct mnt_namespace *ns = kls->ns;
5978+
u64 mnt_parent_id = kls->mnt_parent_id;
5979+
u64 last_mnt_id = kls->last_mnt_id;
5980+
u64 *mnt_ids = kls->kmnt_ids;
5981+
size_t nr_mnt_ids = kls->nr_mnt_ids;
59715982
struct path orig;
59725983
struct mount *r, *first;
59735984
ssize_t ret;
59745985

59755986
rwsem_assert_held(&namespace_sem);
59765987

5977-
ret = grab_requested_root(ns, &root);
5988+
ret = grab_requested_root(ns, &kls->root);
59785989
if (ret)
59795990
return ret;
59805991

59815992
if (mnt_parent_id == LSMT_ROOT) {
5982-
orig = root;
5993+
orig = kls->root;
59835994
} else {
59845995
orig.mnt = lookup_mnt_in_ns(mnt_parent_id, ns);
59855996
if (!orig.mnt)
@@ -5991,7 +6002,7 @@ static ssize_t do_listmount(struct mnt_namespace *ns, u64 mnt_parent_id,
59916002
* Don't trigger audit denials. We just want to determine what
59926003
* mounts to show users.
59936004
*/
5994-
if (!is_path_reachable(real_mount(orig.mnt), orig.dentry, &root) &&
6005+
if (!is_path_reachable(real_mount(orig.mnt), orig.dentry, &kls->root) &&
59956006
!ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
59966007
return -EPERM;
59976008

@@ -6024,14 +6035,45 @@ static ssize_t do_listmount(struct mnt_namespace *ns, u64 mnt_parent_id,
60246035
return ret;
60256036
}
60266037

6038+
static void __free_klistmount_free(const struct klistmount *kls)
6039+
{
6040+
path_put(&kls->root);
6041+
kvfree(kls->kmnt_ids);
6042+
mnt_ns_release(kls->ns);
6043+
}
6044+
6045+
static inline int prepare_klistmount(struct klistmount *kls, struct mnt_id_req *kreq,
6046+
size_t nr_mnt_ids)
6047+
{
6048+
6049+
u64 last_mnt_id = kreq->param;
6050+
6051+
/* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */
6052+
if (last_mnt_id != 0 && last_mnt_id <= MNT_UNIQUE_ID_OFFSET)
6053+
return -EINVAL;
6054+
6055+
kls->last_mnt_id = last_mnt_id;
6056+
6057+
kls->nr_mnt_ids = nr_mnt_ids;
6058+
kls->kmnt_ids = kvmalloc_array(nr_mnt_ids, sizeof(*kls->kmnt_ids),
6059+
GFP_KERNEL_ACCOUNT);
6060+
if (!kls->kmnt_ids)
6061+
return -ENOMEM;
6062+
6063+
kls->ns = grab_requested_mnt_ns(kreq);
6064+
if (!kls->ns)
6065+
return -ENOENT;
6066+
6067+
kls->mnt_parent_id = kreq->mnt_id;
6068+
return 0;
6069+
}
6070+
60276071
SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
60286072
u64 __user *, mnt_ids, size_t, nr_mnt_ids, unsigned int, flags)
60296073
{
6030-
u64 *kmnt_ids __free(kvfree) = NULL;
6074+
struct klistmount kls __free(klistmount_free) = {};
60316075
const size_t maxcount = 1000000;
6032-
struct mnt_namespace *ns __free(mnt_ns_release) = NULL;
60336076
struct mnt_id_req kreq;
6034-
u64 last_mnt_id;
60356077
ssize_t ret;
60366078

60376079
if (flags & ~LISTMOUNT_REVERSE)
@@ -6052,35 +6094,24 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
60526094
if (ret)
60536095
return ret;
60546096

6055-
last_mnt_id = kreq.param;
6056-
/* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */
6057-
if (last_mnt_id != 0 && last_mnt_id <= MNT_UNIQUE_ID_OFFSET)
6058-
return -EINVAL;
6059-
6060-
kmnt_ids = kvmalloc_array(nr_mnt_ids, sizeof(*kmnt_ids),
6061-
GFP_KERNEL_ACCOUNT);
6062-
if (!kmnt_ids)
6063-
return -ENOMEM;
6064-
6065-
ns = grab_requested_mnt_ns(&kreq);
6066-
if (!ns)
6067-
return -ENOENT;
6097+
ret = prepare_klistmount(&kls, &kreq, nr_mnt_ids);
6098+
if (ret)
6099+
return ret;
60686100

6069-
if (kreq.mnt_ns_id && (ns != current->nsproxy->mnt_ns) &&
6070-
!ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
6101+
if (kreq.mnt_ns_id && (kls.ns != current->nsproxy->mnt_ns) &&
6102+
!ns_capable_noaudit(kls.ns->user_ns, CAP_SYS_ADMIN))
60716103
return -ENOENT;
60726104

60736105
/*
60746106
* We only need to guard against mount topology changes as
60756107
* listmount() doesn't care about any mount properties.
60766108
*/
60776109
scoped_guard(rwsem_read, &namespace_sem)
6078-
ret = do_listmount(ns, kreq.mnt_id, last_mnt_id, kmnt_ids,
6079-
nr_mnt_ids, (flags & LISTMOUNT_REVERSE));
6110+
ret = do_listmount(&kls, (flags & LISTMOUNT_REVERSE));
60806111
if (ret <= 0)
60816112
return ret;
60826113

6083-
if (copy_to_user(mnt_ids, kmnt_ids, ret * sizeof(*mnt_ids)))
6114+
if (copy_to_user(mnt_ids, kls.kmnt_ids, ret * sizeof(*mnt_ids)))
60846115
return -EFAULT;
60856116

60866117
return ret;

0 commit comments

Comments
 (0)