diff --git a/kernel/nstree.c b/kernel/nstree.c index 58b2b55fa8d2..a756ae2e7881 100644 --- a/kernel/nstree.c +++ b/kernel/nstree.c @@ -408,15 +408,12 @@ struct klistns { u32 ns_type; struct user_namespace *user_ns; bool userns_capable; - struct ns_common *first_ns; }; static void __free_klistns_free(const struct klistns *kls) { if (kls->user_ns_id != LISTNS_CURRENT_USER) put_user_ns(kls->user_ns); - if (kls->first_ns && kls->first_ns->ops) - kls->first_ns->ops->put(kls->first_ns); } #define NS_ALL (PID_NS | USER_NS | MNT_NS | UTS_NS | IPC_NS | NET_NS | CGROUP_NS | TIME_NS) @@ -461,6 +458,9 @@ static inline int prepare_klistns(struct klistns *kls, struct ns_id_req *kreq, /* * Lookup a namespace owned by owner with id > ns_id. * Returns the namespace with the smallest id that is > ns_id. + * + * No reference is taken. The caller must hold rcu_read_lock() and may only + * use the result as the starting point of an RCU list walk. */ static struct ns_common *lookup_ns_owner_after(u64 ns_id, struct ns_common *owner) { @@ -484,8 +484,6 @@ static struct ns_common *lookup_ns_owner_after(u64 ns_id, struct ns_common *owne } } - if (ret) - ret = ns_get_unless_inactive(ret); return ret; } @@ -558,25 +556,27 @@ static ssize_t do_listns_userns(struct klistns *kls) return -EINVAL; kls->user_ns = to_user_ns(ns); - /* - * Use the rbtree to find the first namespace we care about and - * then use it's list entry to iterate from there. - */ - if (kls->last_ns_id) { - kls->first_ns = lookup_ns_owner_after(kls->last_ns_id, ns); - if (!kls->first_ns) - return -ENOENT; - first_ns = kls->first_ns; - } - ret = 0; head = &to_ns_common(kls->user_ns)->ns_owner_root.ns_list_head; kls->userns_capable = may_see_all_namespaces(); rcu_read_lock(); - if (!first_ns) + /* + * Use the rbtree to find the first namespace we care about and + * then use it's list entry to iterate from there. That namespace + * is filtered like any other, so the walk doesn't end early just + * because it is inactive or already dying. + */ + if (kls->last_ns_id) { + first_ns = lookup_ns_owner_after(kls->last_ns_id, ns); + if (!first_ns) { + rcu_read_unlock(); + return -ENOENT; + } + } else { first_ns = list_entry_rcu(head->next, typeof(*first_ns), ns_owner_node.ns_list_entry); + } ns = first_ns; list_for_each_entry_from_rcu(ns, head, ns_owner_node.ns_list_entry) { @@ -613,6 +613,9 @@ static ssize_t do_listns_userns(struct klistns *kls) /* * Lookup a namespace with id > ns_id in either the unified tree or a type-specific tree. * Returns the namespace with the smallest id that is > ns_id. + * + * No reference is taken. The caller must hold rcu_read_lock() and may only + * use the result as the starting point of an RCU list walk. */ static struct ns_common *lookup_ns_id_after(u64 ns_id, int ns_type) { @@ -649,8 +652,6 @@ static struct ns_common *lookup_ns_id_after(u64 ns_id, int ns_type) } } - if (ret) - ret = ns_get_unless_inactive(ret); return ret; } @@ -700,13 +701,6 @@ static ssize_t do_listns(struct klistns *kls) return -EINVAL; } - if (kls->last_ns_id) { - kls->first_ns = lookup_ns_id_after(kls->last_ns_id, ns_type); - if (!kls->first_ns) - return -ENOENT; - first_ns = kls->first_ns; - } - ret = 0; if (ns_tree) head = &ns_tree->ns_list_head; @@ -715,8 +709,15 @@ static ssize_t do_listns(struct klistns *kls) rcu_read_lock(); - if (!first_ns) + if (kls->last_ns_id) { + first_ns = lookup_ns_id_after(kls->last_ns_id, ns_type); + if (!first_ns) { + rcu_read_unlock(); + return -ENOENT; + } + } else { first_ns = first_ns_common(head, ns_tree); + } for (ns = first_ns; !ns_common_is_head(ns, head, ns_tree) && nr_ns_ids; ns = next_ns_common(ns, ns_tree)) {