summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/nscommon.c52
-rw-r--r--kernel/nstree.c44
2 files changed, 60 insertions, 36 deletions
diff --git a/kernel/nscommon.c b/kernel/nscommon.c
index 6fe1c747fa46..c910b979e433 100644
--- a/kernel/nscommon.c
+++ b/kernel/nscommon.c
@@ -54,7 +54,7 @@ static void ns_debug(struct ns_common *ns, const struct proc_ns_operations *ops)
int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_operations *ops, int inum)
{
- int ret;
+ int ret = 0;
refcount_set(&ns->__ns_ref, 1);
ns->stashed = NULL;
@@ -74,11 +74,10 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
ns_debug(ns, ops);
#endif
- if (inum) {
+ if (inum)
ns->inum = inum;
- return 0;
- }
- ret = proc_alloc_inum(&ns->inum);
+ else
+ ret = proc_alloc_inum(&ns->inum);
if (ret)
return ret;
/*
@@ -115,13 +114,6 @@ struct ns_common *__must_check ns_owner(struct ns_common *ns)
return to_ns_common(owner);
}
-void __ns_ref_active_get_owner(struct ns_common *ns)
-{
- ns = ns_owner(ns);
- if (ns)
- WARN_ON_ONCE(atomic_add_negative(1, &ns->__ns_ref_active));
-}
-
/*
* The active reference count works by having each namespace that gets
* created take a single active reference on its owning user namespace.
@@ -172,14 +164,29 @@ void __ns_ref_active_get_owner(struct ns_common *ns)
* The iteration stops once we reach a namespace that still has active
* references.
*/
-void __ns_ref_active_put_owner(struct ns_common *ns)
+void __ns_ref_active_put(struct ns_common *ns)
{
+ /* Initial namespaces are always active. */
+ if (is_ns_init_id(ns))
+ return;
+
+ if (!atomic_dec_and_test(&ns->__ns_ref_active)) {
+ VFS_WARN_ON_ONCE(__ns_ref_active_read(ns) < 0);
+ return;
+ }
+
+ VFS_WARN_ON_ONCE(is_ns_init_id(ns));
+ VFS_WARN_ON_ONCE(!__ns_ref_read(ns));
+
for (;;) {
ns = ns_owner(ns);
if (!ns)
return;
- if (!atomic_dec_and_test(&ns->__ns_ref_active))
+ VFS_WARN_ON_ONCE(is_ns_init_id(ns));
+ if (!atomic_dec_and_test(&ns->__ns_ref_active)) {
+ VFS_WARN_ON_ONCE(__ns_ref_active_read(ns) < 0);
return;
+ }
}
}
@@ -275,10 +282,18 @@ void __ns_ref_active_put_owner(struct ns_common *ns)
* it also needs to take another reference on its owning user namespace
* and so on.
*/
-void __ns_ref_active_resurrect(struct ns_common *ns)
+void __ns_ref_active_get(struct ns_common *ns)
{
+ int prev;
+
+ /* Initial namespaces are always active. */
+ if (is_ns_init_id(ns))
+ return;
+
/* If we didn't resurrect the namespace we're done. */
- if (atomic_fetch_add(1, &ns->__ns_ref_active))
+ prev = atomic_fetch_add(1, &ns->__ns_ref_active);
+ VFS_WARN_ON_ONCE(prev < 0);
+ if (likely(prev))
return;
/*
@@ -290,7 +305,10 @@ void __ns_ref_active_resurrect(struct ns_common *ns)
if (!ns)
return;
- if (atomic_fetch_add(1, &ns->__ns_ref_active))
+ VFS_WARN_ON_ONCE(is_ns_init_id(ns));
+ prev = atomic_fetch_add(1, &ns->__ns_ref_active);
+ VFS_WARN_ON_ONCE(prev < 0);
+ if (likely(prev))
return;
}
}
diff --git a/kernel/nstree.c b/kernel/nstree.c
index 4a8838683b6b..97404fb90749 100644
--- a/kernel/nstree.c
+++ b/kernel/nstree.c
@@ -173,14 +173,6 @@ void __ns_tree_add_raw(struct ns_common *ns, struct ns_tree *ns_tree)
write_sequnlock(&ns_tree_lock);
VFS_WARN_ON_ONCE(node);
-
- /*
- * Take an active reference on the owner namespace. This ensures
- * that the owner remains visible while any of its child namespaces
- * are active. For init namespaces this is a no-op as ns_owner()
- * returns NULL for namespaces owned by init_user_ns.
- */
- __ns_ref_active_get_owner(ns);
}
void __ns_tree_remove(struct ns_common *ns, struct ns_tree *ns_tree)
@@ -505,13 +497,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 +527,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 +560,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 +571,14 @@ static ssize_t do_listns_userns(struct klistns *kls)
rcu_read_unlock();
- if (put_user(valid->ns_id, ns_ids + ret))
- return -EINVAL;
+ ns_put(prev);
+ prev = valid;
+
+ if (put_user(valid->ns_id, ns_ids + ret)) {
+ ns_put(prev);
+ return -EFAULT;
+ }
+
nr_ns_ids--;
ret++;
@@ -587,6 +586,7 @@ static ssize_t do_listns_userns(struct klistns *kls)
}
rcu_read_unlock();
+ ns_put(prev);
return ret;
}
@@ -668,7 +668,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 +705,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 +713,13 @@ static ssize_t do_listns(struct klistns *kls)
rcu_read_unlock();
- if (put_user(valid->ns_id, ns_ids + ret))
- return -EINVAL;
+ ns_put(prev);
+ prev = valid;
+
+ if (put_user(valid->ns_id, ns_ids + ret)) {
+ ns_put(prev);
+ return -EFAULT;
+ }
nr_ns_ids--;
ret++;
@@ -723,6 +728,7 @@ static ssize_t do_listns(struct klistns *kls)
}
rcu_read_unlock();
+ ns_put(prev);
return ret;
}