summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2025-11-09 22:11:24 +0100
committerChristian Brauner <brauner@kernel.org>2025-11-10 10:20:53 +0100
commit2ec2aff3c8e2523f3bde90e78031bae811335f3c (patch)
treed151c1cc662695ad0db6ce4b6b04748431befa76
parent7cd3d204412b0584df38fd7be20002137f34721a (diff)
ns: make sure reference are dropped outside of rcu lock
The mount namespace may in fact sleep when putting the last passive reference so we need to drop the namespace reference outside of the rcu read lock. Do this by delaying the put until the next iteration where we've already moved on to the next namespace and legitimized it. Once we drop the rcu read lock to call put_user() we will also drop the reference to the previous namespace in the tree. Link: https://patch.msgid.link/20251109-namespace-6-19-fixes-v1-3-ae8a4ad5a3b3@kernel.org Fixes: 76b6f5dfb3fd ("nstree: add listns()") Signed-off-by: Christian Brauner <brauner@kernel.org>
-rw-r--r--kernel/nstree.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/kernel/nstree.c b/kernel/nstree.c
index 4a8838683b6b..55b72d4f8de4 100644
--- a/kernel/nstree.c
+++ b/kernel/nstree.c
@@ -505,13 +505,13 @@ static inline bool __must_check may_list_ns(const struct klistns *kls,
return false;
}
-static void __ns_put(struct ns_common *ns)
+static inline void ns_put(struct ns_common *ns)
{
- if (ns->ops)
+ if (ns && ns->ops)
ns->ops->put(ns);
}
-DEFINE_FREE(ns_put, struct ns_common *, if (!IS_ERR_OR_NULL(_T)) __ns_put(_T))
+DEFINE_FREE(ns_put, struct ns_common *, if (!IS_ERR_OR_NULL(_T)) ns_put(_T))
static inline struct ns_common *__must_check legitimize_ns(const struct klistns *kls,
struct ns_common *candidate)
@@ -535,7 +535,7 @@ static ssize_t do_listns_userns(struct klistns *kls)
{
u64 __user *ns_ids = kls->uns_ids;
size_t nr_ns_ids = kls->nr_ns_ids;
- struct ns_common *ns = NULL, *first_ns = NULL;
+ struct ns_common *ns = NULL, *first_ns = NULL, *prev = NULL;
const struct list_head *head;
ssize_t ret;
@@ -568,9 +568,10 @@ static ssize_t do_listns_userns(struct klistns *kls)
if (!first_ns)
first_ns = list_entry_rcu(head->next, typeof(*ns), ns_owner_entry);
+
for (ns = first_ns; &ns->ns_owner_entry != head && nr_ns_ids;
ns = list_entry_rcu(ns->ns_owner_entry.next, typeof(*ns), ns_owner_entry)) {
- struct ns_common *valid __free(ns_put);
+ struct ns_common *valid;
valid = legitimize_ns(kls, ns);
if (!valid)
@@ -578,8 +579,14 @@ static ssize_t do_listns_userns(struct klistns *kls)
rcu_read_unlock();
- if (put_user(valid->ns_id, ns_ids + ret))
+ ns_put(prev);
+ prev = valid;
+
+ if (put_user(valid->ns_id, ns_ids + ret)) {
+ ns_put(prev);
return -EINVAL;
+ }
+
nr_ns_ids--;
ret++;
@@ -587,6 +594,7 @@ static ssize_t do_listns_userns(struct klistns *kls)
}
rcu_read_unlock();
+ ns_put(prev);
return ret;
}
@@ -668,7 +676,7 @@ static ssize_t do_listns(struct klistns *kls)
{
u64 __user *ns_ids = kls->uns_ids;
size_t nr_ns_ids = kls->nr_ns_ids;
- struct ns_common *ns, *first_ns = NULL;
+ struct ns_common *ns, *first_ns = NULL, *prev = NULL;
struct ns_tree *ns_tree = NULL;
const struct list_head *head;
u32 ns_type;
@@ -705,7 +713,7 @@ static ssize_t do_listns(struct klistns *kls)
for (ns = first_ns; !ns_common_is_head(ns, head, ns_tree) && nr_ns_ids;
ns = next_ns_common(ns, ns_tree)) {
- struct ns_common *valid __free(ns_put);
+ struct ns_common *valid;
valid = legitimize_ns(kls, ns);
if (!valid)
@@ -713,8 +721,13 @@ static ssize_t do_listns(struct klistns *kls)
rcu_read_unlock();
- if (put_user(valid->ns_id, ns_ids + ret))
+ ns_put(prev);
+ prev = valid;
+
+ if (put_user(valid->ns_id, ns_ids + ret)) {
+ ns_put(prev);
return -EINVAL;
+ }
nr_ns_ids--;
ret++;
@@ -723,6 +736,7 @@ static ssize_t do_listns(struct klistns *kls)
}
rcu_read_unlock();
+ ns_put(prev);
return ret;
}