mirror of https://github.com/torvalds/linux.git
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: 76b6f5dfb3 ("nstree: add listns()")
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
parent
7cd3d20441
commit
2ec2aff3c8
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue