[PATCH v2 00/10] userns: sysctl limits for namespaces

STALE3671d

23 messages, 5 authors, 2016-07-26 · open the first message on its own page

[PATCH v2 00/10] userns: sysctl limits for namespaces

From: Eric W. Biederman <hidden>
Date: 2016-07-21 16:39:35

This patchset addresses two use cases:
- Implement a sane upper bound on the number of namespaces.
- Provide a way for sandboxes to limit the attack surface from
  namespaces.

The maximum sane case I can imagine is if every process is a fat
process, so I set the maximum number of namespaces to the maximum
number of threads.

I make these limits recursive and per user namespace so that a
usernamespace root can reduce the limits further.  If a user namespace
root raises the limit the limit in the parent namespace will be honored.

I have cut this implementation to the bare minimum needed to achieve
these objectives.

Does anyone know if there is a proper error code to return for resource
limit exceeded?  I am currently using -EUSERS or -ENFILE but both of
those feel a little wrong.

Assuming nothing problematic shows up in the review I will add these to
my user namespace tree.

These patches are also available at:
    git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git for-testing

Changes since v1:
- Compilation issues with !CONFIG_SYSCTL and !CONFIG_USER_NS have been addressed
- Comment improvements.
- A few names have been changed to be clearer.

Eric W. Biederman (10):
      sysctl: Stop implicitly passing current into sysctl_table_root.lookup
      userns: Add per user namespace sysctls.
      userns: Add a limit on the number of user namespaces
      userns: Generalize the user namespace count into ucount
      pidns: Add a limit on the number of pid namespaces
      utsns: Add a limit on the number of uts namespaces
      ipcns: Add a  limit on the number of ipc namespaces
      cgroupns: Add a limit on the number of cgroup namespaces
      netns: Add a limit on the number of net namespaces
      mntns: Add a limit on the number of mount namespaces.

 fs/namespace.c                 |  19 ++++-
 fs/proc/proc_sysctl.c          |  14 +--
 include/linux/sysctl.h         |   3 +-
 include/linux/user_namespace.h |  40 +++++++++
 ipc/namespace.c                |  42 ++++++---
 kernel/cgroup.c                |  15 ++++
 kernel/fork.c                  |   5 ++
 kernel/pid_namespace.c         |  22 ++++-
 kernel/user_namespace.c        | 188 ++++++++++++++++++++++++++++++++++++++---
 kernel/utsname.c               |  31 +++++--
 net/core/net_namespace.c       |  15 ++++
 net/sysctl_net.c               |   4 +-
 12 files changed, 355 insertions(+), 43 deletions(-)

Eric

[PATCH v2 01/10] sysctl: Stop implicitly passing current into sysctl_table_root.lookup

From: Eric W. Biederman <hidden>
Date: 2016-07-21 16:40:05

Passing nsproxy into sysctl_table_root.lookup was a premature
optimization in attempt to avoid depending on current.  The
directory /proc/self/sys has not appeared and if and when
it does this code will need to be reviewed closely and reworked
anyway.  So remove the premature optimization.

Acked-by: Serge Hallyn <redacted>
Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
---
 fs/proc/proc_sysctl.c  | 14 +++++++-------
 include/linux/sysctl.h |  3 +--
 net/sysctl_net.c       |  4 ++--
 3 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 5e57c3e46e1d..534630687489 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -72,7 +72,7 @@ static DEFINE_SPINLOCK(sysctl_lock);
 
 static void drop_sysctl_table(struct ctl_table_header *header);
 static int sysctl_follow_link(struct ctl_table_header **phead,
-	struct ctl_table **pentry, struct nsproxy *namespaces);
+	struct ctl_table **pentry);
 static int insert_links(struct ctl_table_header *head);
 static void put_links(struct ctl_table_header *header);
 
@@ -319,11 +319,11 @@ static void sysctl_head_finish(struct ctl_table_header *head)
 }
 
 static struct ctl_table_set *
-lookup_header_set(struct ctl_table_root *root, struct nsproxy *namespaces)
+lookup_header_set(struct ctl_table_root *root)
 {
 	struct ctl_table_set *set = &root->default_set;
 	if (root->lookup)
-		set = root->lookup(root, namespaces);
+		set = root->lookup(root);
 	return set;
 }
 
@@ -491,7 +491,7 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
 		goto out;
 
 	if (S_ISLNK(p->mode)) {
-		ret = sysctl_follow_link(&h, &p, current->nsproxy);
+		ret = sysctl_follow_link(&h, &p);
 		err = ERR_PTR(ret);
 		if (ret)
 			goto out;
@@ -659,7 +659,7 @@ static bool proc_sys_link_fill_cache(struct file *file,
 
 	if (S_ISLNK(table->mode)) {
 		/* It is not an error if we can not follow the link ignore it */
-		int err = sysctl_follow_link(&head, &table, current->nsproxy);
+		int err = sysctl_follow_link(&head, &table);
 		if (err)
 			goto out;
 	}
@@ -976,7 +976,7 @@ static struct ctl_dir *xlate_dir(struct ctl_table_set *set, struct ctl_dir *dir)
 }
 
 static int sysctl_follow_link(struct ctl_table_header **phead,
-	struct ctl_table **pentry, struct nsproxy *namespaces)
+	struct ctl_table **pentry)
 {
 	struct ctl_table_header *head;
 	struct ctl_table_root *root;
@@ -988,7 +988,7 @@ static int sysctl_follow_link(struct ctl_table_header **phead,
 	ret = 0;
 	spin_lock(&sysctl_lock);
 	root = (*pentry)->data;
-	set = lookup_header_set(root, namespaces);
+	set = lookup_header_set(root);
 	dir = xlate_dir(set, (*phead)->parent);
 	if (IS_ERR(dir))
 		ret = PTR_ERR(dir);
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index fa7bc29925c9..6385b331f2b9 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -154,8 +154,7 @@ struct ctl_table_set {
 
 struct ctl_table_root {
 	struct ctl_table_set default_set;
-	struct ctl_table_set *(*lookup)(struct ctl_table_root *root,
-					   struct nsproxy *namespaces);
+	struct ctl_table_set *(*lookup)(struct ctl_table_root *root);
 	int (*permissions)(struct ctl_table_header *head, struct ctl_table *table);
 };
 
diff --git a/net/sysctl_net.c b/net/sysctl_net.c
index ed98c1fc3de1..2951f229a855 100644
--- a/net/sysctl_net.c
+++ b/net/sysctl_net.c
@@ -27,9 +27,9 @@
 #endif
 
 static struct ctl_table_set *
-net_ctl_header_lookup(struct ctl_table_root *root, struct nsproxy *namespaces)
+net_ctl_header_lookup(struct ctl_table_root *root)
 {
-	return &namespaces->net_ns->sysctls;
+	return &current->nsproxy->net_ns->sysctls;
 }
 
 static int is_seen(struct ctl_table_set *set)
-- 
2.8.3

[PATCH v2 02/10] userns: Add per user namespace sysctls.

From: Eric W. Biederman <hidden>
Date: 2016-07-21 16:40:06

Limit per userns sysctls to only be opened for write by a holder
of CAP_SYS_RESOURCE.

Add all of the necessary boilerplate for having per user namespace
sysctls.

Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
---
 include/linux/user_namespace.h |  4 ++
 kernel/user_namespace.c        | 96 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 98 insertions(+), 2 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 9217169c64cb..7d59af1f08f1 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -39,6 +39,10 @@ struct user_namespace {
 	struct key		*persistent_keyring_register;
 	struct rw_semaphore	persistent_keyring_register_sem;
 #endif
+#ifdef CONFIG_SYSCTL
+	struct ctl_table_set	set;
+	struct ctl_table_header *sysctls;
+#endif
 };
 
 extern struct user_namespace init_user_ns;
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 68f594212759..10afbb55dfc2 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -30,6 +30,70 @@ static bool new_idmap_permitted(const struct file *file,
 				struct user_namespace *ns, int cap_setid,
 				struct uid_gid_map *map);
 
+#ifdef CONFIG_SYSCTL
+static struct ctl_table_set *
+set_lookup(struct ctl_table_root *root)
+{
+	return &current_user_ns()->set;
+}
+
+static int set_is_seen(struct ctl_table_set *set)
+{
+	return &current_user_ns()->set == set;
+}
+
+static int set_permissions(struct ctl_table_header *head,
+				  struct ctl_table *table)
+{
+	struct user_namespace *user_ns =
+		container_of(head->set, struct user_namespace, set);
+	int mode;
+
+	/* Allow users with CAP_SYS_RESOURCE unrestrained access */
+	if (ns_capable(user_ns, CAP_SYS_RESOURCE))
+		mode = (table->mode & S_IRWXU) >> 6;
+	else
+	/* Allow all others at most read-only access */
+		mode = table->mode & S_IROTH;
+	return (mode << 6) | (mode << 3) | mode;
+}
+
+static struct ctl_table_root set_root = {
+	.lookup = set_lookup,
+	.permissions = set_permissions,
+};
+
+static struct ctl_table userns_table[] = {
+	{ }
+};
+#endif /* CONFIG_SYSCTL */
+
+static bool setup_userns_sysctls(struct user_namespace *ns)
+{
+#ifdef CONFIG_SYSCTL
+	struct ctl_table *tbl;
+	setup_sysctl_set(&ns->set, &set_root, set_is_seen);
+	tbl = kmemdup(userns_table, sizeof(userns_table), GFP_KERNEL);
+	if (tbl) {
+		ns->sysctls = __register_sysctl_table(&ns->set, "userns", tbl);
+	}
+	if (!ns->sysctls) {
+		kfree(tbl);
+		retire_sysctl_set(&ns->set);
+		return false;
+	}
+#endif
+	return true;
+}
+
+static void retire_userns_sysctls(struct user_namespace *ns)
+{
+#ifdef CONFIG_SYSCTL
+	unregister_sysctl_table(ns->sysctls);
+	retire_sysctl_set(&ns->set);
+#endif
+}
+
 static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
 {
 	/* Start with the same capabilities as init but useless for doing
@@ -107,12 +171,22 @@ int create_user_ns(struct cred *new)
 	ns->flags = parent_ns->flags;
 	mutex_unlock(&userns_state_mutex);
 
-	set_cred_user_ns(new, ns);
-
 #ifdef CONFIG_PERSISTENT_KEYRINGS
 	init_rwsem(&ns->persistent_keyring_register_sem);
 #endif
+	ret = -ENOMEM;
+	if (!setup_userns_sysctls(ns))
+		goto fail_keyring;
+
+	set_cred_user_ns(new, ns);
 	return 0;
+fail_keyring:
+#ifdef CONFIG_PERSISTENT_KEYRINGS
+	key_put(ns->persistent_keyring_register);
+#endif
+	ns_free_inum(&ns->ns);
+	kmem_cache_free(user_ns_cachep, ns);
+	return ret;
 }
 
 int unshare_userns(unsigned long unshare_flags, struct cred **new_cred)
@@ -141,6 +215,7 @@ void free_user_ns(struct user_namespace *ns)
 
 	do {
 		parent = ns->parent;
+		retire_userns_sysctls(ns);
 #ifdef CONFIG_PERSISTENT_KEYRINGS
 		key_put(ns->persistent_keyring_register);
 #endif
@@ -1012,9 +1087,26 @@ const struct proc_ns_operations userns_operations = {
 	.install	= userns_install,
 };
 
+static __init void user_namespace_sysctl_init(void)
+{
+#ifdef CONFIG_SYSCTL
+	static struct ctl_table_header *userns_header;
+	static struct ctl_table empty[1];
+	/*
+	 * It is necessary to register the userns directory in the
+	 * default set so that registrations in the child sets work
+	 * properly.
+	 */
+	userns_header = register_sysctl("userns", empty);
+	BUG_ON(!userns_header);
+	BUG_ON(!setup_userns_sysctls(&init_user_ns));
+#endif
+}
+
 static __init int user_namespaces_init(void)
 {
 	user_ns_cachep = KMEM_CACHE(user_namespace, SLAB_PANIC);
+	user_namespace_sysctl_init();
 	return 0;
 }
 subsys_initcall(user_namespaces_init);
-- 
2.8.3

[PATCH v2 04/10] userns: Generalize the user namespace count into ucount

From: Eric W. Biederman <hidden>
Date: 2016-07-21 16:40:08

The same kind of recursive sane default limit and policy
countrol that has been implemented for the user namespace
is desirable for the other namespaces, so generalize
the user namespace refernce count into a ucount.

Signed-off-by: "Eric W. Biederman" <redacted>
---
 include/linux/user_namespace.h | 32 ++++++++++++++++++++++--
 kernel/fork.c                  |  5 +++-
 kernel/user_namespace.c        | 55 +++++++++++++++++++++++++++---------------
 3 files changed, 69 insertions(+), 23 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index ba6a995178f9..f74a0facc696 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -22,6 +22,16 @@ struct uid_gid_map {	/* 64 bytes -- 1 cache line */
 
 #define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED
 
+enum ucounts {
+	UCOUNT_USER_NAMESPACES,
+	UCOUNT_COUNTS,
+};
+
+struct ucount {
+	int max;
+	atomic_t count;
+};
+
 struct user_namespace {
 	struct uid_gid_map	uid_map;
 	struct uid_gid_map	gid_map;
@@ -43,8 +53,7 @@ struct user_namespace {
 	struct ctl_table_set	set;
 	struct ctl_table_header *sysctls;
 #endif
-	int max_user_namespaces;
-	atomic_t user_namespaces;
+	struct ucount ucount[UCOUNT_COUNTS];
 };
 
 extern struct user_namespace init_user_ns;
@@ -79,6 +88,8 @@ extern ssize_t proc_setgroups_write(struct file *, const char __user *, size_t,
 extern int proc_setgroups_show(struct seq_file *m, void *v);
 extern bool userns_may_setgroups(const struct user_namespace *ns);
 extern bool current_in_userns(const struct user_namespace *target_ns);
+extern bool inc_ucount(struct user_namespace *ns, enum ucounts type);
+extern void dec_ucount(struct user_namespace *ns, enum ucounts type);
 #else
 
 static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
@@ -112,6 +123,23 @@ static inline bool current_in_userns(const struct user_namespace *target_ns)
 {
 	return true;
 }
+
+static inline bool inc_ucount(struct user_namespace *ns, enum ucounts type)
+{
+	int max = READ_ONCE(init_user_ns.ucount[type].max);
+	int sum = atomic_inc_return(&init_user_ns.ucount[type].count);
+	if (sum > max) {
+		atomic_dec(&init_user_ns.ucount[type].count);
+		return false;
+	}
+	return true;
+}
+
+static inline void dec_ucount(struct user_namespace *ns, enum ucounts type)
+{
+	int dec = atomic_dec_if_positive(&init_user_ns.ucount[type].count);
+	WARN_ON_ONCE(dec < 0);
+}
 #endif
 
 #endif /* _LINUX_USER_H */
diff --git a/kernel/fork.c b/kernel/fork.c
index 95d5498c463f..30ca5eb4ca1e 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -304,6 +304,7 @@ int arch_task_struct_size __read_mostly;
 
 void __init fork_init(void)
 {
+	int i;
 #ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR
 #ifndef ARCH_MIN_TASKALIGN
 #define ARCH_MIN_TASKALIGN	L1_CACHE_BYTES
@@ -324,7 +325,9 @@ void __init fork_init(void)
 	init_task.signal->rlim[RLIMIT_SIGPENDING] =
 		init_task.signal->rlim[RLIMIT_NPROC];
 
-	init_user_ns.max_user_namespaces = max_threads;
+	for (i = 0; i < UCOUNT_COUNTS; i++) {
+		init_user_ns.ucount[i].max = max_threads;
+	}
 }
 
 int __weak arch_dup_task_struct(struct task_struct *dst,
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 0061550e3282..728d7e4995ff 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -66,16 +66,17 @@ static struct ctl_table_root set_root = {
 
 static int zero = 0;
 static int count_max = COUNT_MAX;
+#define UCOUNT_ENTRY(name) 				\
+	{						\
+		.procname	= name,			\
+		.maxlen		= sizeof(int),		\
+		.mode		= 0644,			\
+		.proc_handler	= proc_dointvec_minmax,	\
+		.extra1		= &zero,		\
+		.extra2		= &count_max,		\
+	}
 static struct ctl_table userns_table[] = {
-	{
-		.procname	= "max_user_namespaces",
-		.data		= &init_user_ns.max_user_namespaces,
-		.maxlen		= sizeof(init_user_ns.max_user_namespaces),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &zero,
-		.extra2		= &count_max,
-	},
+	UCOUNT_ENTRY("max_user_namespaces"),
 	{ }
 };
 #endif /* CONFIG_SYSCTL */
@@ -87,8 +88,10 @@ static bool setup_userns_sysctls(struct user_namespace *ns)
 	setup_sysctl_set(&ns->set, &set_root, set_is_seen);
 	tbl = kmemdup(userns_table, sizeof(userns_table), GFP_KERNEL);
 	if (tbl) {
-		tbl[0].data = &ns->max_user_namespaces;
-
+		int i;
+		for (i = 0; i < UCOUNT_COUNTS; i++) {
+			tbl[i].data = &ns->ucount[i].max;
+		}
 		ns->sysctls = __register_sysctl_table(&ns->set, "userns", tbl);
 	}
 	if (!ns->sysctls) {
@@ -127,34 +130,44 @@ static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
 	cred->user_ns = user_ns;
 }
 
-static bool inc_user_namespaces(struct user_namespace *ns)
+bool inc_ucount(struct user_namespace *ns, enum ucounts type)
 {
 	struct user_namespace *pos, *bad;
 	for (pos = ns; pos; pos = pos->parent) {
-		int max = READ_ONCE(pos->max_user_namespaces);
-		int sum = atomic_inc_return(&pos->user_namespaces);
+		int max = READ_ONCE(pos->ucount[type].max);
+		int sum = atomic_inc_return(&pos->ucount[type].count);
 		if (sum > max)
 			goto fail;
 	}
 	return true;
 fail:
 	bad = pos;
-	atomic_dec(&pos->user_namespaces);
+	atomic_dec(&pos->ucount[type].count);
 	for (pos = ns; pos != bad; pos = pos->parent)
-		atomic_dec(&pos->user_namespaces);
+		atomic_dec(&pos->ucount[type].count);
 
 	return false;
 }
 
-static void dec_user_namespaces(struct user_namespace *ns)
+void dec_ucount(struct user_namespace *ns, enum ucounts type)
 {
 	struct user_namespace *pos;
 	for (pos = ns; pos; pos = pos->parent) {
-		int dec = atomic_dec_if_positive(&pos->user_namespaces);
+		int dec = atomic_dec_if_positive(&pos->ucount[type].count);
 		WARN_ON_ONCE(dec < 0);
 	}
 }
 
+static bool inc_user_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_USER_NAMESPACES);
+}
+
+static void dec_user_namespaces(struct user_namespace *ns)
+{
+	return dec_ucount(ns, UCOUNT_USER_NAMESPACES);
+}
+
 /*
  * Create a new user namespace, deriving the creator from the user in the
  * passed credentials, and replacing that user with the new root user for the
@@ -168,7 +181,7 @@ int create_user_ns(struct cred *new)
 	struct user_namespace *ns, *parent_ns = new->user_ns;
 	kuid_t owner = new->euid;
 	kgid_t group = new->egid;
-	int ret;
+	int ret, i;
 
 	ret = -EUSERS;
 	if (parent_ns->level > 32)
@@ -212,7 +225,9 @@ int create_user_ns(struct cred *new)
 	ns->level = parent_ns->level + 1;
 	ns->owner = owner;
 	ns->group = group;
-	ns->max_user_namespaces = COUNT_MAX;
+	for (i = 0; i < UCOUNT_COUNTS; i++) {
+		ns->ucount[i].max = COUNT_MAX;
+	}
 
 	/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
 	mutex_lock(&userns_state_mutex);
-- 
2.8.3

[PATCH v2 05/10] pidns: Add a limit on the number of pid namespaces

From: Eric W. Biederman <hidden>
Date: 2016-07-21 16:40:09

Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
---
 include/linux/user_namespace.h |  1 +
 kernel/pid_namespace.c         | 22 ++++++++++++++++++----
 kernel/user_namespace.c        |  1 +
 3 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index f74a0facc696..47733637741a 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -24,6 +24,7 @@ struct uid_gid_map {	/* 64 bytes -- 1 cache line */
 
 enum ucounts {
 	UCOUNT_USER_NAMESPACES,
+	UCOUNT_PID_NAMESPACES,
 	UCOUNT_COUNTS,
 };
 
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index a65ba137fd15..049cc14ae37a 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -79,6 +79,16 @@ static void proc_cleanup_work(struct work_struct *work)
 /* MAX_PID_NS_LEVEL is needed for limiting size of 'struct pid' */
 #define MAX_PID_NS_LEVEL 32
 
+static bool inc_pid_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_PID_NAMESPACES);
+}
+
+static void dec_pid_namespaces(struct user_namespace *ns)
+{
+	dec_ucount(ns, UCOUNT_PID_NAMESPACES);
+}
+
 static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns,
 	struct pid_namespace *parent_pid_ns)
 {
@@ -87,15 +97,16 @@ static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns
 	int i;
 	int err;
 
-	if (level > MAX_PID_NS_LEVEL) {
-		err = -EINVAL;
+	err = -EINVAL;
+	if (level > MAX_PID_NS_LEVEL)
+		goto out;
+	if (!inc_pid_namespaces(user_ns))
 		goto out;
-	}
 
 	err = -ENOMEM;
 	ns = kmem_cache_zalloc(pid_ns_cachep, GFP_KERNEL);
 	if (ns == NULL)
-		goto out;
+		goto out_dec;
 
 	ns->pidmap[0].page = kzalloc(PAGE_SIZE, GFP_KERNEL);
 	if (!ns->pidmap[0].page)
@@ -129,6 +140,8 @@ out_free_map:
 	kfree(ns->pidmap[0].page);
 out_free:
 	kmem_cache_free(pid_ns_cachep, ns);
+out_dec:
+	dec_pid_namespaces(user_ns);
 out:
 	return ERR_PTR(err);
 }
@@ -146,6 +159,7 @@ static void destroy_pid_namespace(struct pid_namespace *ns)
 	ns_free_inum(&ns->ns);
 	for (i = 0; i < PIDMAP_ENTRIES; i++)
 		kfree(ns->pidmap[i].page);
+	dec_pid_namespaces(ns->user_ns);
 	put_user_ns(ns->user_ns);
 	call_rcu(&ns->rcu, delayed_free_pidns);
 }
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 728d7e4995ff..02a03ead7afc 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -77,6 +77,7 @@ static int count_max = COUNT_MAX;
 	}
 static struct ctl_table userns_table[] = {
 	UCOUNT_ENTRY("max_user_namespaces"),
+	UCOUNT_ENTRY("max_pid_namespaces"),
 	{ }
 };
 #endif /* CONFIG_SYSCTL */
-- 
2.8.3

[PATCH v2 06/10] utsns: Add a limit on the number of uts namespaces

From: Eric W. Biederman <hidden>
Date: 2016-07-21 16:40:10

Signed-off-by: "Eric W. Biederman" <redacted>
---
 include/linux/user_namespace.h |  1 +
 kernel/user_namespace.c        |  1 +
 kernel/utsname.c               | 31 ++++++++++++++++++++++++++-----
 3 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 47733637741a..bed2506081fe 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -25,6 +25,7 @@ struct uid_gid_map {	/* 64 bytes -- 1 cache line */
 enum ucounts {
 	UCOUNT_USER_NAMESPACES,
 	UCOUNT_PID_NAMESPACES,
+	UCOUNT_UTS_NAMESPACES,
 	UCOUNT_COUNTS,
 };
 
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 02a03ead7afc..6b205c24e888 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -78,6 +78,7 @@ static int count_max = COUNT_MAX;
 static struct ctl_table userns_table[] = {
 	UCOUNT_ENTRY("max_user_namespaces"),
 	UCOUNT_ENTRY("max_pid_namespaces"),
+	UCOUNT_ENTRY("max_uts_namespaces"),
 	{ }
 };
 #endif /* CONFIG_SYSCTL */
diff --git a/kernel/utsname.c b/kernel/utsname.c
index 831ea7108232..4f13c0419d64 100644
--- a/kernel/utsname.c
+++ b/kernel/utsname.c
@@ -17,6 +17,16 @@
 #include <linux/user_namespace.h>
 #include <linux/proc_ns.h>
 
+static bool inc_uts_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_UTS_NAMESPACES);
+}
+
+static void dec_uts_namespaces(struct user_namespace *ns)
+{
+	dec_ucount(ns, UCOUNT_UTS_NAMESPACES);
+}
+
 static struct uts_namespace *create_uts_ns(void)
 {
 	struct uts_namespace *uts_ns;
@@ -38,15 +48,18 @@ static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns,
 	struct uts_namespace *ns;
 	int err;
 
+	err = -ENFILE;
+	if (!inc_uts_namespaces(user_ns))
+		goto fail;
+
+	err = -ENOMEM;
 	ns = create_uts_ns();
 	if (!ns)
-		return ERR_PTR(-ENOMEM);
+		goto fail_dec;
 
 	err = ns_alloc_inum(&ns->ns);
-	if (err) {
-		kfree(ns);
-		return ERR_PTR(err);
-	}
+	if (err)
+		goto fail_free;
 
 	ns->ns.ops = &utsns_operations;
 
@@ -55,6 +68,13 @@ static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns,
 	ns->user_ns = get_user_ns(user_ns);
 	up_read(&uts_sem);
 	return ns;
+
+fail_free:
+	kfree(ns);
+fail_dec:
+	dec_uts_namespaces(user_ns);
+fail:
+	return ERR_PTR(err);
 }
 
 /*
@@ -85,6 +105,7 @@ void free_uts_ns(struct kref *kref)
 	struct uts_namespace *ns;
 
 	ns = container_of(kref, struct uts_namespace, kref);
+	dec_uts_namespaces(ns->user_ns);
 	put_user_ns(ns->user_ns);
 	ns_free_inum(&ns->ns);
 	kfree(ns);
-- 
2.8.3

[PATCH v2 09/10] netns: Add a limit on the number of net namespaces

From: Eric W. Biederman <hidden>
Date: 2016-07-21 16:40:13

Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
---
 include/linux/user_namespace.h |  1 +
 kernel/user_namespace.c        |  1 +
 net/core/net_namespace.c       | 15 +++++++++++++++
 3 files changed, 17 insertions(+)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 1a3a9cb93277..f86afa536baf 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -27,6 +27,7 @@ enum ucounts {
 	UCOUNT_PID_NAMESPACES,
 	UCOUNT_UTS_NAMESPACES,
 	UCOUNT_IPC_NAMESPACES,
+	UCOUNT_NET_NAMESPACES,
 	UCOUNT_CGROUP_NAMESPACES,
 	UCOUNT_COUNTS,
 };
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 1cf074cb47e2..e326ca722ae0 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -80,6 +80,7 @@ static struct ctl_table userns_table[] = {
 	UCOUNT_ENTRY("max_pid_namespaces"),
 	UCOUNT_ENTRY("max_uts_namespaces"),
 	UCOUNT_ENTRY("max_ipc_namespaces"),
+	UCOUNT_ENTRY("max_net_namespaces"),
 	UCOUNT_ENTRY("max_cgroup_namespaces"),
 	{ }
 };
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 2c2eb1b629b1..a489f192d619 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -266,6 +266,16 @@ struct net *get_net_ns_by_id(struct net *net, int id)
 	return peer;
 }
 
+static bool inc_net_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_NET_NAMESPACES);
+}
+
+static void dec_net_namespaces(struct user_namespace *ns)
+{
+	dec_ucount(ns, UCOUNT_NET_NAMESPACES);
+}
+
 /*
  * setup_net runs the initializers for the network namespace object.
  */
@@ -276,6 +286,9 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
 	int error = 0;
 	LIST_HEAD(net_exit_list);
 
+	if (!inc_net_namespaces(user_ns))
+		return -ENFILE;
+
 	atomic_set(&net->count, 1);
 	atomic_set(&net->passive, 1);
 	net->dev_base_seq = 1;
@@ -372,6 +385,7 @@ struct net *copy_net_ns(unsigned long flags,
 	}
 	mutex_unlock(&net_mutex);
 	if (rv < 0) {
+		dec_net_namespaces(user_ns);
 		put_user_ns(user_ns);
 		net_drop_ns(net);
 		return ERR_PTR(rv);
@@ -444,6 +458,7 @@ static void cleanup_net(struct work_struct *work)
 	/* Finally it is safe to free my network namespace structure */
 	list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) {
 		list_del_init(&net->exit_list);
+		dec_net_namespaces(net->user_ns);
 		put_user_ns(net->user_ns);
 		net_drop_ns(net);
 	}
-- 
2.8.3

[PATCH v2 10/10] mntns: Add a limit on the number of mount namespaces.

From: Eric W. Biederman <hidden>
Date: 2016-07-21 16:40:14

Signed-off-by: "Eric W. Biederman" <redacted>
---
 fs/namespace.c                 | 19 ++++++++++++++++++-
 include/linux/user_namespace.h |  1 +
 kernel/user_namespace.c        |  1 +
 3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index aabe8e397fc3..3942ae6c34f5 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2718,9 +2718,20 @@ dput_out:
 	return retval;
 }
 
+static bool inc_mnt_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_MNT_NAMESPACES);
+}
+
+static void dec_mnt_namespaces(struct user_namespace *ns)
+{
+	dec_ucount(ns, UCOUNT_MNT_NAMESPACES);
+}
+
 static void free_mnt_ns(struct mnt_namespace *ns)
 {
 	ns_free_inum(&ns->ns);
+	dec_mnt_namespaces(ns->user_ns);
 	put_user_ns(ns->user_ns);
 	kfree(ns);
 }
@@ -2739,12 +2750,18 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns)
 	struct mnt_namespace *new_ns;
 	int ret;
 
+	if (!inc_mnt_namespaces(user_ns))
+		return ERR_PTR(-ENFILE);
+
 	new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
-	if (!new_ns)
+	if (!new_ns) {
+		dec_mnt_namespaces(user_ns);
 		return ERR_PTR(-ENOMEM);
+	}
 	ret = ns_alloc_inum(&new_ns->ns);
 	if (ret) {
 		kfree(new_ns);
+		dec_mnt_namespaces(user_ns);
 		return ERR_PTR(ret);
 	}
 	new_ns->ns.ops = &mntns_operations;
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index f86afa536baf..ee0e9d7b2e67 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -28,6 +28,7 @@ enum ucounts {
 	UCOUNT_UTS_NAMESPACES,
 	UCOUNT_IPC_NAMESPACES,
 	UCOUNT_NET_NAMESPACES,
+	UCOUNT_MNT_NAMESPACES,
 	UCOUNT_CGROUP_NAMESPACES,
 	UCOUNT_COUNTS,
 };
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index e326ca722ae0..f6702d84afab 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -81,6 +81,7 @@ static struct ctl_table userns_table[] = {
 	UCOUNT_ENTRY("max_uts_namespaces"),
 	UCOUNT_ENTRY("max_ipc_namespaces"),
 	UCOUNT_ENTRY("max_net_namespaces"),
+	UCOUNT_ENTRY("max_mnt_namespaces"),
 	UCOUNT_ENTRY("max_cgroup_namespaces"),
 	{ }
 };
-- 
2.8.3

[PATCH v2 03/10] userns: Add a limit on the number of user namespaces

From: Eric W. Biederman <hidden>
Date: 2016-07-21 16:53:49

Export the export the maximum number of user namespaces as
/proc/sys/userns/max_user_namespaces.

Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
---
 include/linux/user_namespace.h |  2 ++
 kernel/fork.c                  |  2 ++
 kernel/user_namespace.c        | 69 +++++++++++++++++++++++++++++++++++++-----
 3 files changed, 65 insertions(+), 8 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 7d59af1f08f1..ba6a995178f9 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -43,6 +43,8 @@ struct user_namespace {
 	struct ctl_table_set	set;
 	struct ctl_table_header *sysctls;
 #endif
+	int max_user_namespaces;
+	atomic_t user_namespaces;
 };
 
 extern struct user_namespace init_user_ns;
diff --git a/kernel/fork.c b/kernel/fork.c
index 5c2c355aa97f..95d5498c463f 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -323,6 +323,8 @@ void __init fork_init(void)
 	init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
 	init_task.signal->rlim[RLIMIT_SIGPENDING] =
 		init_task.signal->rlim[RLIMIT_NPROC];
+
+	init_user_ns.max_user_namespaces = max_threads;
 }
 
 int __weak arch_dup_task_struct(struct task_struct *dst,
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 10afbb55dfc2..0061550e3282 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -29,6 +29,7 @@ static DEFINE_MUTEX(userns_state_mutex);
 static bool new_idmap_permitted(const struct file *file,
 				struct user_namespace *ns, int cap_setid,
 				struct uid_gid_map *map);
+#define COUNT_MAX (INT_MAX - 1)
 
 #ifdef CONFIG_SYSCTL
 static struct ctl_table_set *
@@ -63,7 +64,18 @@ static struct ctl_table_root set_root = {
 	.permissions = set_permissions,
 };
 
+static int zero = 0;
+static int count_max = COUNT_MAX;
 static struct ctl_table userns_table[] = {
+	{
+		.procname	= "max_user_namespaces",
+		.data		= &init_user_ns.max_user_namespaces,
+		.maxlen		= sizeof(init_user_ns.max_user_namespaces),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= &zero,
+		.extra2		= &count_max,
+	},
 	{ }
 };
 #endif /* CONFIG_SYSCTL */
@@ -75,6 +87,8 @@ static bool setup_userns_sysctls(struct user_namespace *ns)
 	setup_sysctl_set(&ns->set, &set_root, set_is_seen);
 	tbl = kmemdup(userns_table, sizeof(userns_table), GFP_KERNEL);
 	if (tbl) {
+		tbl[0].data = &ns->max_user_namespaces;
+
 		ns->sysctls = __register_sysctl_table(&ns->set, "userns", tbl);
 	}
 	if (!ns->sysctls) {
@@ -113,6 +127,34 @@ static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
 	cred->user_ns = user_ns;
 }
 
+static bool inc_user_namespaces(struct user_namespace *ns)
+{
+	struct user_namespace *pos, *bad;
+	for (pos = ns; pos; pos = pos->parent) {
+		int max = READ_ONCE(pos->max_user_namespaces);
+		int sum = atomic_inc_return(&pos->user_namespaces);
+		if (sum > max)
+			goto fail;
+	}
+	return true;
+fail:
+	bad = pos;
+	atomic_dec(&pos->user_namespaces);
+	for (pos = ns; pos != bad; pos = pos->parent)
+		atomic_dec(&pos->user_namespaces);
+
+	return false;
+}
+
+static void dec_user_namespaces(struct user_namespace *ns)
+{
+	struct user_namespace *pos;
+	for (pos = ns; pos; pos = pos->parent) {
+		int dec = atomic_dec_if_positive(&pos->user_namespaces);
+		WARN_ON_ONCE(dec < 0);
+	}
+}
+
 /*
  * Create a new user namespace, deriving the creator from the user in the
  * passed credentials, and replacing that user with the new root user for the
@@ -128,8 +170,12 @@ int create_user_ns(struct cred *new)
 	kgid_t group = new->egid;
 	int ret;
 
+	ret = -EUSERS;
 	if (parent_ns->level > 32)
-		return -EUSERS;
+		goto fail;
+
+	if (!inc_user_namespaces(parent_ns))
+		goto fail;
 
 	/*
 	 * Verify that we can not violate the policy of which files
@@ -137,26 +183,27 @@ int create_user_ns(struct cred *new)
 	 * by verifing that the root directory is at the root of the
 	 * mount namespace which allows all files to be accessed.
 	 */
+	ret = -EPERM;
 	if (current_chrooted())
-		return -EPERM;
+		goto fail_dec;
 
 	/* The creator needs a mapping in the parent user namespace
 	 * or else we won't be able to reasonably tell userspace who
 	 * created a user_namespace.
 	 */
+	ret = -EPERM;
 	if (!kuid_has_mapping(parent_ns, owner) ||
 	    !kgid_has_mapping(parent_ns, group))
-		return -EPERM;
+		goto fail_dec;
 
+	ret = -ENOMEM;
 	ns = kmem_cache_zalloc(user_ns_cachep, GFP_KERNEL);
 	if (!ns)
-		return -ENOMEM;
+		goto fail_dec;
 
 	ret = ns_alloc_inum(&ns->ns);
-	if (ret) {
-		kmem_cache_free(user_ns_cachep, ns);
-		return ret;
-	}
+	if (ret)
+		goto fail_free;
 	ns->ns.ops = &userns_operations;
 
 	atomic_set(&ns->count, 1);
@@ -165,6 +212,7 @@ int create_user_ns(struct cred *new)
 	ns->level = parent_ns->level + 1;
 	ns->owner = owner;
 	ns->group = group;
+	ns->max_user_namespaces = COUNT_MAX;
 
 	/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
 	mutex_lock(&userns_state_mutex);
@@ -185,7 +233,11 @@ fail_keyring:
 	key_put(ns->persistent_keyring_register);
 #endif
 	ns_free_inum(&ns->ns);
+fail_free:
 	kmem_cache_free(user_ns_cachep, ns);
+fail_dec:
+	dec_user_namespaces(parent_ns);
+fail:
 	return ret;
 }
 
@@ -221,6 +273,7 @@ void free_user_ns(struct user_namespace *ns)
 #endif
 		ns_free_inum(&ns->ns);
 		kmem_cache_free(user_ns_cachep, ns);
+		dec_user_namespaces(parent);
 		ns = parent;
 	} while (atomic_dec_and_test(&parent->count));
 }
-- 
2.8.3

[PATCH v2 07/10] ipcns: Add a limit on the number of ipc namespaces

From: Eric W. Biederman <hidden>
Date: 2016-07-21 16:54:00

Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
---
 include/linux/user_namespace.h |  1 +
 ipc/namespace.c                | 42 +++++++++++++++++++++++++++++++-----------
 kernel/user_namespace.c        |  1 +
 3 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index bed2506081fe..367cf08ff63d 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -26,6 +26,7 @@ enum ucounts {
 	UCOUNT_USER_NAMESPACES,
 	UCOUNT_PID_NAMESPACES,
 	UCOUNT_UTS_NAMESPACES,
+	UCOUNT_IPC_NAMESPACES,
 	UCOUNT_COUNTS,
 };
 
diff --git a/ipc/namespace.c b/ipc/namespace.c
index 04cb07eb81f1..3996a1e41a1d 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -16,33 +16,42 @@
 
 #include "util.h"
 
+static bool inc_ipc_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_IPC_NAMESPACES);
+}
+
+static void dec_ipc_namespaces(struct user_namespace *ns)
+{
+	dec_ucount(ns, UCOUNT_IPC_NAMESPACES);
+}
+
 static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
 					   struct ipc_namespace *old_ns)
 {
 	struct ipc_namespace *ns;
 	int err;
 
+	err = -ENFILE;
+	if (!inc_ipc_namespaces(user_ns))
+		goto fail;
+
+	err = -ENOMEM;
 	ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL);
 	if (ns == NULL)
-		return ERR_PTR(-ENOMEM);
+		goto fail_dec;
 
 	err = ns_alloc_inum(&ns->ns);
-	if (err) {
-		kfree(ns);
-		return ERR_PTR(err);
-	}
+	if (err)
+		goto fail_free;
 	ns->ns.ops = &ipcns_operations;
 
 	atomic_set(&ns->count, 1);
 	ns->user_ns = get_user_ns(user_ns);
 
 	err = mq_init_ns(ns);
-	if (err) {
-		put_user_ns(ns->user_ns);
-		ns_free_inum(&ns->ns);
-		kfree(ns);
-		return ERR_PTR(err);
-	}
+	if (err)
+		goto fail_put;
 	atomic_inc(&nr_ipc_ns);
 
 	sem_init_ns(ns);
@@ -50,6 +59,16 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
 	shm_init_ns(ns);
 
 	return ns;
+
+fail_put:
+	put_user_ns(ns->user_ns);
+	ns_free_inum(&ns->ns);
+fail_free:
+	kfree(ns);
+fail_dec:
+	dec_ipc_namespaces(user_ns);
+fail:
+	return ERR_PTR(err);
 }
 
 struct ipc_namespace *copy_ipcs(unsigned long flags,
@@ -98,6 +117,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)
 	shm_exit_ns(ns);
 	atomic_dec(&nr_ipc_ns);
 
+	dec_ipc_namespaces(ns->user_ns);
 	put_user_ns(ns->user_ns);
 	ns_free_inum(&ns->ns);
 	kfree(ns);
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 6b205c24e888..060d3e099f87 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -79,6 +79,7 @@ static struct ctl_table userns_table[] = {
 	UCOUNT_ENTRY("max_user_namespaces"),
 	UCOUNT_ENTRY("max_pid_namespaces"),
 	UCOUNT_ENTRY("max_uts_namespaces"),
+	UCOUNT_ENTRY("max_ipc_namespaces"),
 	{ }
 };
 #endif /* CONFIG_SYSCTL */
-- 
2.8.3

[PATCH v2 08/10] cgroupns: Add a limit on the number of cgroup namespaces

From: Eric W. Biederman <hidden>
Date: 2016-07-21 16:54:06

Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
---
 include/linux/user_namespace.h |  1 +
 kernel/cgroup.c                | 15 +++++++++++++++
 kernel/user_namespace.c        |  1 +
 3 files changed, 17 insertions(+)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 367cf08ff63d..1a3a9cb93277 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -27,6 +27,7 @@ enum ucounts {
 	UCOUNT_PID_NAMESPACES,
 	UCOUNT_UTS_NAMESPACES,
 	UCOUNT_IPC_NAMESPACES,
+	UCOUNT_CGROUP_NAMESPACES,
 	UCOUNT_COUNTS,
 };
 
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 86cb5c6e8932..240eec43390b 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -6260,6 +6260,16 @@ void cgroup_sk_free(struct sock_cgroup_data *skcd)
 
 /* cgroup namespaces */
 
+static bool inc_cgroup_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_CGROUP_NAMESPACES);
+}
+
+static void dec_cgroup_namespaces(struct user_namespace *ns)
+{
+	dec_ucount(ns, UCOUNT_CGROUP_NAMESPACES);
+}
+
 static struct cgroup_namespace *alloc_cgroup_ns(void)
 {
 	struct cgroup_namespace *new_ns;
@@ -6281,6 +6291,7 @@ static struct cgroup_namespace *alloc_cgroup_ns(void)
 void free_cgroup_ns(struct cgroup_namespace *ns)
 {
 	put_css_set(ns->root_cset);
+	dec_cgroup_namespaces(ns->user_ns);
 	put_user_ns(ns->user_ns);
 	ns_free_inum(&ns->ns);
 	kfree(ns);
@@ -6305,6 +6316,9 @@ struct cgroup_namespace *copy_cgroup_ns(unsigned long flags,
 	if (!ns_capable(user_ns, CAP_SYS_ADMIN))
 		return ERR_PTR(-EPERM);
 
+	if (!inc_cgroup_namespaces(user_ns))
+		return ERR_PTR(-ENFILE);
+
 	mutex_lock(&cgroup_mutex);
 	spin_lock_bh(&css_set_lock);
 
@@ -6317,6 +6331,7 @@ struct cgroup_namespace *copy_cgroup_ns(unsigned long flags,
 	new_ns = alloc_cgroup_ns();
 	if (IS_ERR(new_ns)) {
 		put_css_set(cset);
+		dec_cgroup_namespaces(user_ns);
 		return new_ns;
 	}
 
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 060d3e099f87..1cf074cb47e2 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -80,6 +80,7 @@ static struct ctl_table userns_table[] = {
 	UCOUNT_ENTRY("max_pid_namespaces"),
 	UCOUNT_ENTRY("max_uts_namespaces"),
 	UCOUNT_ENTRY("max_ipc_namespaces"),
+	UCOUNT_ENTRY("max_cgroup_namespaces"),
 	{ }
 };
 #endif /* CONFIG_SYSCTL */
-- 
2.8.3

Re: [PATCH v2 00/10] userns: sysctl limits for namespaces

From: Colin Walters <hidden>
Date: 2016-07-22 13:33:19

On Thu, Jul 21, 2016, at 12:39 PM, Eric W. Biederman wrote:
This patchset addresses two use cases:
- Implement a sane upper bound on the number of namespaces.
- Provide a way for sandboxes to limit the attack surface from
  namespaces.
Perhaps this is obvious, but since you didn't quite explicitly state it;
do you see this as obsoleting the existing downstream patches
mentioned in:
https://lwn.net/Articles/673597/
It seems conceptually similar to Kees' original approach, right?

The high level makes sense to me...most interesting is
per-userns sysctls.  I'll note most current container managers
mount /proc/sys read-only, and Docker specifically drops
CAP_SYS_RESOURCE by default, so they'd likely need to learn
how to undo that if one wanted to support recursive container usage.
We'd probably need to evaluate the safety of having /proc/sys
writable generally.  (Also it's rather common to filter out CLONE_NEWUSER
via seccomp, but that's easy to undo)

But that's the flip side - if we're aiming primarily for an upstreamable
way to *limit* namespace usage, it seems sane to me.

Re: [PATCH v2 03/10] userns: Add a limit on the number of user namespaces

From: "Serge E. Hallyn" <serge@hallyn.com>
Date: 2016-07-25 23:05:16

Quoting Eric W. Biederman (ebiederm@xmission.com):
Export the export the maximum number of user namespaces as
^ note if you resend, duplicate "export the"
/proc/sys/userns/max_user_namespaces.

Signed-off-by: "Eric W. Biederman" <redacted>
Acked-by: Serge Hallyn <serge@hallyn.com>
quoted hunk
---
 include/linux/user_namespace.h |  2 ++
 kernel/fork.c                  |  2 ++
 kernel/user_namespace.c        | 69 +++++++++++++++++++++++++++++++++++++-----
 3 files changed, 65 insertions(+), 8 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 7d59af1f08f1..ba6a995178f9 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -43,6 +43,8 @@ struct user_namespace {
 	struct ctl_table_set	set;
 	struct ctl_table_header *sysctls;
 #endif
+	int max_user_namespaces;
+	atomic_t user_namespaces;
 };
 
 extern struct user_namespace init_user_ns;
diff --git a/kernel/fork.c b/kernel/fork.c
index 5c2c355aa97f..95d5498c463f 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -323,6 +323,8 @@ void __init fork_init(void)
 	init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
 	init_task.signal->rlim[RLIMIT_SIGPENDING] =
 		init_task.signal->rlim[RLIMIT_NPROC];
+
+	init_user_ns.max_user_namespaces = max_threads;
 }
 
 int __weak arch_dup_task_struct(struct task_struct *dst,
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 10afbb55dfc2..0061550e3282 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -29,6 +29,7 @@ static DEFINE_MUTEX(userns_state_mutex);
 static bool new_idmap_permitted(const struct file *file,
 				struct user_namespace *ns, int cap_setid,
 				struct uid_gid_map *map);
+#define COUNT_MAX (INT_MAX - 1)
 
 #ifdef CONFIG_SYSCTL
 static struct ctl_table_set *
@@ -63,7 +64,18 @@ static struct ctl_table_root set_root = {
 	.permissions = set_permissions,
 };
 
+static int zero = 0;
+static int count_max = COUNT_MAX;
 static struct ctl_table userns_table[] = {
+	{
+		.procname	= "max_user_namespaces",
+		.data		= &init_user_ns.max_user_namespaces,
+		.maxlen		= sizeof(init_user_ns.max_user_namespaces),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= &zero,
+		.extra2		= &count_max,
+	},
 	{ }
 };
 #endif /* CONFIG_SYSCTL */
@@ -75,6 +87,8 @@ static bool setup_userns_sysctls(struct user_namespace *ns)
 	setup_sysctl_set(&ns->set, &set_root, set_is_seen);
 	tbl = kmemdup(userns_table, sizeof(userns_table), GFP_KERNEL);
 	if (tbl) {
+		tbl[0].data = &ns->max_user_namespaces;
+
 		ns->sysctls = __register_sysctl_table(&ns->set, "userns", tbl);
 	}
 	if (!ns->sysctls) {
@@ -113,6 +127,34 @@ static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
 	cred->user_ns = user_ns;
 }
 
+static bool inc_user_namespaces(struct user_namespace *ns)
+{
+	struct user_namespace *pos, *bad;
+	for (pos = ns; pos; pos = pos->parent) {
+		int max = READ_ONCE(pos->max_user_namespaces);
+		int sum = atomic_inc_return(&pos->user_namespaces);
+		if (sum > max)
+			goto fail;
+	}
+	return true;
+fail:
+	bad = pos;
+	atomic_dec(&pos->user_namespaces);
+	for (pos = ns; pos != bad; pos = pos->parent)
+		atomic_dec(&pos->user_namespaces);
+
+	return false;
+}
+
+static void dec_user_namespaces(struct user_namespace *ns)
+{
+	struct user_namespace *pos;
+	for (pos = ns; pos; pos = pos->parent) {
+		int dec = atomic_dec_if_positive(&pos->user_namespaces);
+		WARN_ON_ONCE(dec < 0);
+	}
+}
+
 /*
  * Create a new user namespace, deriving the creator from the user in the
  * passed credentials, and replacing that user with the new root user for the
@@ -128,8 +170,12 @@ int create_user_ns(struct cred *new)
 	kgid_t group = new->egid;
 	int ret;
 
+	ret = -EUSERS;
 	if (parent_ns->level > 32)
-		return -EUSERS;
+		goto fail;
+
+	if (!inc_user_namespaces(parent_ns))
+		goto fail;
 
 	/*
 	 * Verify that we can not violate the policy of which files
@@ -137,26 +183,27 @@ int create_user_ns(struct cred *new)
 	 * by verifing that the root directory is at the root of the
 	 * mount namespace which allows all files to be accessed.
 	 */
+	ret = -EPERM;
 	if (current_chrooted())
-		return -EPERM;
+		goto fail_dec;
 
 	/* The creator needs a mapping in the parent user namespace
 	 * or else we won't be able to reasonably tell userspace who
 	 * created a user_namespace.
 	 */
+	ret = -EPERM;
 	if (!kuid_has_mapping(parent_ns, owner) ||
 	    !kgid_has_mapping(parent_ns, group))
-		return -EPERM;
+		goto fail_dec;
 
+	ret = -ENOMEM;
 	ns = kmem_cache_zalloc(user_ns_cachep, GFP_KERNEL);
 	if (!ns)
-		return -ENOMEM;
+		goto fail_dec;
 
 	ret = ns_alloc_inum(&ns->ns);
-	if (ret) {
-		kmem_cache_free(user_ns_cachep, ns);
-		return ret;
-	}
+	if (ret)
+		goto fail_free;
 	ns->ns.ops = &userns_operations;
 
 	atomic_set(&ns->count, 1);
@@ -165,6 +212,7 @@ int create_user_ns(struct cred *new)
 	ns->level = parent_ns->level + 1;
 	ns->owner = owner;
 	ns->group = group;
+	ns->max_user_namespaces = COUNT_MAX;
 
 	/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
 	mutex_lock(&userns_state_mutex);
@@ -185,7 +233,11 @@ fail_keyring:
 	key_put(ns->persistent_keyring_register);
 #endif
 	ns_free_inum(&ns->ns);
+fail_free:
 	kmem_cache_free(user_ns_cachep, ns);
+fail_dec:
+	dec_user_namespaces(parent_ns);
+fail:
 	return ret;
 }
 
@@ -221,6 +273,7 @@ void free_user_ns(struct user_namespace *ns)
 #endif
 		ns_free_inum(&ns->ns);
 		kmem_cache_free(user_ns_cachep, ns);
+		dec_user_namespaces(parent);
 		ns = parent;
 	} while (atomic_dec_and_test(&parent->count));
 }
-- 
2.8.3

Re: [PATCH v2 04/10] userns: Generalize the user namespace count into ucount

From: "Serge E. Hallyn" <serge@hallyn.com>
Date: 2016-07-25 23:09:07

Quoting Eric W. Biederman (ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org):
The same kind of recursive sane default limit and policy
countrol that has been implemented for the user namespace
is desirable for the other namespaces, so generalize
the user namespace refernce count into a ucount.

Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
Acked-by: Serge Hallyn <redacted>
quoted hunk
---
 include/linux/user_namespace.h | 32 ++++++++++++++++++++++--
 kernel/fork.c                  |  5 +++-
 kernel/user_namespace.c        | 55 +++++++++++++++++++++++++++---------------
 3 files changed, 69 insertions(+), 23 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index ba6a995178f9..f74a0facc696 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -22,6 +22,16 @@ struct uid_gid_map {	/* 64 bytes -- 1 cache line */
 
 #define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED
 
+enum ucounts {
+	UCOUNT_USER_NAMESPACES,
+	UCOUNT_COUNTS,
+};
+
+struct ucount {
+	int max;
+	atomic_t count;
+};
+
 struct user_namespace {
 	struct uid_gid_map	uid_map;
 	struct uid_gid_map	gid_map;
@@ -43,8 +53,7 @@ struct user_namespace {
 	struct ctl_table_set	set;
 	struct ctl_table_header *sysctls;
 #endif
-	int max_user_namespaces;
-	atomic_t user_namespaces;
+	struct ucount ucount[UCOUNT_COUNTS];
 };
 
 extern struct user_namespace init_user_ns;
@@ -79,6 +88,8 @@ extern ssize_t proc_setgroups_write(struct file *, const char __user *, size_t,
 extern int proc_setgroups_show(struct seq_file *m, void *v);
 extern bool userns_may_setgroups(const struct user_namespace *ns);
 extern bool current_in_userns(const struct user_namespace *target_ns);
+extern bool inc_ucount(struct user_namespace *ns, enum ucounts type);
+extern void dec_ucount(struct user_namespace *ns, enum ucounts type);
 #else
 
 static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
@@ -112,6 +123,23 @@ static inline bool current_in_userns(const struct user_namespace *target_ns)
 {
 	return true;
 }
+
+static inline bool inc_ucount(struct user_namespace *ns, enum ucounts type)
+{
+	int max = READ_ONCE(init_user_ns.ucount[type].max);
+	int sum = atomic_inc_return(&init_user_ns.ucount[type].count);
+	if (sum > max) {
+		atomic_dec(&init_user_ns.ucount[type].count);
+		return false;
+	}
+	return true;
+}
+
+static inline void dec_ucount(struct user_namespace *ns, enum ucounts type)
+{
+	int dec = atomic_dec_if_positive(&init_user_ns.ucount[type].count);
+	WARN_ON_ONCE(dec < 0);
+}
 #endif
 
 #endif /* _LINUX_USER_H */
diff --git a/kernel/fork.c b/kernel/fork.c
index 95d5498c463f..30ca5eb4ca1e 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -304,6 +304,7 @@ int arch_task_struct_size __read_mostly;
 
 void __init fork_init(void)
 {
+	int i;
 #ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR
 #ifndef ARCH_MIN_TASKALIGN
 #define ARCH_MIN_TASKALIGN	L1_CACHE_BYTES
@@ -324,7 +325,9 @@ void __init fork_init(void)
 	init_task.signal->rlim[RLIMIT_SIGPENDING] =
 		init_task.signal->rlim[RLIMIT_NPROC];
 
-	init_user_ns.max_user_namespaces = max_threads;
+	for (i = 0; i < UCOUNT_COUNTS; i++) {
+		init_user_ns.ucount[i].max = max_threads;
+	}
 }
 
 int __weak arch_dup_task_struct(struct task_struct *dst,
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 0061550e3282..728d7e4995ff 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -66,16 +66,17 @@ static struct ctl_table_root set_root = {
 
 static int zero = 0;
 static int count_max = COUNT_MAX;
+#define UCOUNT_ENTRY(name) 				\
+	{						\
+		.procname	= name,			\
+		.maxlen		= sizeof(int),		\
+		.mode		= 0644,			\
+		.proc_handler	= proc_dointvec_minmax,	\
+		.extra1		= &zero,		\
+		.extra2		= &count_max,		\
+	}
 static struct ctl_table userns_table[] = {
-	{
-		.procname	= "max_user_namespaces",
-		.data		= &init_user_ns.max_user_namespaces,
-		.maxlen		= sizeof(init_user_ns.max_user_namespaces),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &zero,
-		.extra2		= &count_max,
-	},
+	UCOUNT_ENTRY("max_user_namespaces"),
 	{ }
 };
 #endif /* CONFIG_SYSCTL */
@@ -87,8 +88,10 @@ static bool setup_userns_sysctls(struct user_namespace *ns)
 	setup_sysctl_set(&ns->set, &set_root, set_is_seen);
 	tbl = kmemdup(userns_table, sizeof(userns_table), GFP_KERNEL);
 	if (tbl) {
-		tbl[0].data = &ns->max_user_namespaces;
-
+		int i;
+		for (i = 0; i < UCOUNT_COUNTS; i++) {
+			tbl[i].data = &ns->ucount[i].max;
+		}
 		ns->sysctls = __register_sysctl_table(&ns->set, "userns", tbl);
 	}
 	if (!ns->sysctls) {
@@ -127,34 +130,44 @@ static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
 	cred->user_ns = user_ns;
 }
 
-static bool inc_user_namespaces(struct user_namespace *ns)
+bool inc_ucount(struct user_namespace *ns, enum ucounts type)
 {
 	struct user_namespace *pos, *bad;
 	for (pos = ns; pos; pos = pos->parent) {
-		int max = READ_ONCE(pos->max_user_namespaces);
-		int sum = atomic_inc_return(&pos->user_namespaces);
+		int max = READ_ONCE(pos->ucount[type].max);
+		int sum = atomic_inc_return(&pos->ucount[type].count);
 		if (sum > max)
 			goto fail;
 	}
 	return true;
 fail:
 	bad = pos;
-	atomic_dec(&pos->user_namespaces);
+	atomic_dec(&pos->ucount[type].count);
 	for (pos = ns; pos != bad; pos = pos->parent)
-		atomic_dec(&pos->user_namespaces);
+		atomic_dec(&pos->ucount[type].count);
 
 	return false;
 }
 
-static void dec_user_namespaces(struct user_namespace *ns)
+void dec_ucount(struct user_namespace *ns, enum ucounts type)
 {
 	struct user_namespace *pos;
 	for (pos = ns; pos; pos = pos->parent) {
-		int dec = atomic_dec_if_positive(&pos->user_namespaces);
+		int dec = atomic_dec_if_positive(&pos->ucount[type].count);
 		WARN_ON_ONCE(dec < 0);
 	}
 }
 
+static bool inc_user_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_USER_NAMESPACES);
+}
+
+static void dec_user_namespaces(struct user_namespace *ns)
+{
+	return dec_ucount(ns, UCOUNT_USER_NAMESPACES);
+}
+
 /*
  * Create a new user namespace, deriving the creator from the user in the
  * passed credentials, and replacing that user with the new root user for the
@@ -168,7 +181,7 @@ int create_user_ns(struct cred *new)
 	struct user_namespace *ns, *parent_ns = new->user_ns;
 	kuid_t owner = new->euid;
 	kgid_t group = new->egid;
-	int ret;
+	int ret, i;
 
 	ret = -EUSERS;
 	if (parent_ns->level > 32)
@@ -212,7 +225,9 @@ int create_user_ns(struct cred *new)
 	ns->level = parent_ns->level + 1;
 	ns->owner = owner;
 	ns->group = group;
-	ns->max_user_namespaces = COUNT_MAX;
+	for (i = 0; i < UCOUNT_COUNTS; i++) {
+		ns->ucount[i].max = COUNT_MAX;
+	}
 
 	/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */
 	mutex_lock(&userns_state_mutex);
-- 
2.8.3

Re: [PATCH v2 05/10] pidns: Add a limit on the number of pid namespaces

From: "Serge E. Hallyn" <serge@hallyn.com>
Date: 2016-07-25 23:09:13

Quoting Eric W. Biederman (ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org):
Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
Acked-by: Serge Hallyn <redacted>
quoted hunk
---
 include/linux/user_namespace.h |  1 +
 kernel/pid_namespace.c         | 22 ++++++++++++++++++----
 kernel/user_namespace.c        |  1 +
 3 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index f74a0facc696..47733637741a 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -24,6 +24,7 @@ struct uid_gid_map {	/* 64 bytes -- 1 cache line */
 
 enum ucounts {
 	UCOUNT_USER_NAMESPACES,
+	UCOUNT_PID_NAMESPACES,
 	UCOUNT_COUNTS,
 };
 
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index a65ba137fd15..049cc14ae37a 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -79,6 +79,16 @@ static void proc_cleanup_work(struct work_struct *work)
 /* MAX_PID_NS_LEVEL is needed for limiting size of 'struct pid' */
 #define MAX_PID_NS_LEVEL 32
 
+static bool inc_pid_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_PID_NAMESPACES);
+}
+
+static void dec_pid_namespaces(struct user_namespace *ns)
+{
+	dec_ucount(ns, UCOUNT_PID_NAMESPACES);
+}
+
 static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns,
 	struct pid_namespace *parent_pid_ns)
 {
@@ -87,15 +97,16 @@ static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns
 	int i;
 	int err;
 
-	if (level > MAX_PID_NS_LEVEL) {
-		err = -EINVAL;
+	err = -EINVAL;
+	if (level > MAX_PID_NS_LEVEL)
+		goto out;
+	if (!inc_pid_namespaces(user_ns))
 		goto out;
-	}
 
 	err = -ENOMEM;
 	ns = kmem_cache_zalloc(pid_ns_cachep, GFP_KERNEL);
 	if (ns == NULL)
-		goto out;
+		goto out_dec;
 
 	ns->pidmap[0].page = kzalloc(PAGE_SIZE, GFP_KERNEL);
 	if (!ns->pidmap[0].page)
@@ -129,6 +140,8 @@ out_free_map:
 	kfree(ns->pidmap[0].page);
 out_free:
 	kmem_cache_free(pid_ns_cachep, ns);
+out_dec:
+	dec_pid_namespaces(user_ns);
 out:
 	return ERR_PTR(err);
 }
@@ -146,6 +159,7 @@ static void destroy_pid_namespace(struct pid_namespace *ns)
 	ns_free_inum(&ns->ns);
 	for (i = 0; i < PIDMAP_ENTRIES; i++)
 		kfree(ns->pidmap[i].page);
+	dec_pid_namespaces(ns->user_ns);
 	put_user_ns(ns->user_ns);
 	call_rcu(&ns->rcu, delayed_free_pidns);
 }
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 728d7e4995ff..02a03ead7afc 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -77,6 +77,7 @@ static int count_max = COUNT_MAX;
 	}
 static struct ctl_table userns_table[] = {
 	UCOUNT_ENTRY("max_user_namespaces"),
+	UCOUNT_ENTRY("max_pid_namespaces"),
 	{ }
 };
 #endif /* CONFIG_SYSCTL */
-- 
2.8.3

Re: [PATCH v2 06/10] utsns: Add a limit on the number of uts namespaces

From: "Serge E. Hallyn" <serge@hallyn.com>
Date: 2016-07-25 23:09:40

Quoting Eric W. Biederman (ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org):
Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
Acked-by: Serge Hallyn <redacted>
quoted hunk
---
 include/linux/user_namespace.h |  1 +
 kernel/user_namespace.c        |  1 +
 kernel/utsname.c               | 31 ++++++++++++++++++++++++++-----
 3 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 47733637741a..bed2506081fe 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -25,6 +25,7 @@ struct uid_gid_map {	/* 64 bytes -- 1 cache line */
 enum ucounts {
 	UCOUNT_USER_NAMESPACES,
 	UCOUNT_PID_NAMESPACES,
+	UCOUNT_UTS_NAMESPACES,
 	UCOUNT_COUNTS,
 };
 
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 02a03ead7afc..6b205c24e888 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -78,6 +78,7 @@ static int count_max = COUNT_MAX;
 static struct ctl_table userns_table[] = {
 	UCOUNT_ENTRY("max_user_namespaces"),
 	UCOUNT_ENTRY("max_pid_namespaces"),
+	UCOUNT_ENTRY("max_uts_namespaces"),
 	{ }
 };
 #endif /* CONFIG_SYSCTL */
diff --git a/kernel/utsname.c b/kernel/utsname.c
index 831ea7108232..4f13c0419d64 100644
--- a/kernel/utsname.c
+++ b/kernel/utsname.c
@@ -17,6 +17,16 @@
 #include <linux/user_namespace.h>
 #include <linux/proc_ns.h>
 
+static bool inc_uts_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_UTS_NAMESPACES);
+}
+
+static void dec_uts_namespaces(struct user_namespace *ns)
+{
+	dec_ucount(ns, UCOUNT_UTS_NAMESPACES);
+}
+
 static struct uts_namespace *create_uts_ns(void)
 {
 	struct uts_namespace *uts_ns;
@@ -38,15 +48,18 @@ static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns,
 	struct uts_namespace *ns;
 	int err;
 
+	err = -ENFILE;
+	if (!inc_uts_namespaces(user_ns))
+		goto fail;
+
+	err = -ENOMEM;
 	ns = create_uts_ns();
 	if (!ns)
-		return ERR_PTR(-ENOMEM);
+		goto fail_dec;
 
 	err = ns_alloc_inum(&ns->ns);
-	if (err) {
-		kfree(ns);
-		return ERR_PTR(err);
-	}
+	if (err)
+		goto fail_free;
 
 	ns->ns.ops = &utsns_operations;
 
@@ -55,6 +68,13 @@ static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns,
 	ns->user_ns = get_user_ns(user_ns);
 	up_read(&uts_sem);
 	return ns;
+
+fail_free:
+	kfree(ns);
+fail_dec:
+	dec_uts_namespaces(user_ns);
+fail:
+	return ERR_PTR(err);
 }
 
 /*
@@ -85,6 +105,7 @@ void free_uts_ns(struct kref *kref)
 	struct uts_namespace *ns;
 
 	ns = container_of(kref, struct uts_namespace, kref);
+	dec_uts_namespaces(ns->user_ns);
 	put_user_ns(ns->user_ns);
 	ns_free_inum(&ns->ns);
 	kfree(ns);
-- 
2.8.3

Re: [PATCH v2 07/10] ipcns: Add a limit on the number of ipc namespaces

From: "Serge E. Hallyn" <serge@hallyn.com>
Date: 2016-07-25 23:10:32

Quoting Eric W. Biederman (ebiederm@xmission.com):
Signed-off-by: "Eric W. Biederman" <redacted>
Acked-by: Serge Hallyn <serge@hallyn.com>
quoted hunk
---
 include/linux/user_namespace.h |  1 +
 ipc/namespace.c                | 42 +++++++++++++++++++++++++++++++-----------
 kernel/user_namespace.c        |  1 +
 3 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index bed2506081fe..367cf08ff63d 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -26,6 +26,7 @@ enum ucounts {
 	UCOUNT_USER_NAMESPACES,
 	UCOUNT_PID_NAMESPACES,
 	UCOUNT_UTS_NAMESPACES,
+	UCOUNT_IPC_NAMESPACES,
 	UCOUNT_COUNTS,
 };
 
diff --git a/ipc/namespace.c b/ipc/namespace.c
index 04cb07eb81f1..3996a1e41a1d 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -16,33 +16,42 @@
 
 #include "util.h"
 
+static bool inc_ipc_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_IPC_NAMESPACES);
+}
+
+static void dec_ipc_namespaces(struct user_namespace *ns)
+{
+	dec_ucount(ns, UCOUNT_IPC_NAMESPACES);
+}
+
 static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
 					   struct ipc_namespace *old_ns)
 {
 	struct ipc_namespace *ns;
 	int err;
 
+	err = -ENFILE;
+	if (!inc_ipc_namespaces(user_ns))
+		goto fail;
+
+	err = -ENOMEM;
 	ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL);
 	if (ns == NULL)
-		return ERR_PTR(-ENOMEM);
+		goto fail_dec;
 
 	err = ns_alloc_inum(&ns->ns);
-	if (err) {
-		kfree(ns);
-		return ERR_PTR(err);
-	}
+	if (err)
+		goto fail_free;
 	ns->ns.ops = &ipcns_operations;
 
 	atomic_set(&ns->count, 1);
 	ns->user_ns = get_user_ns(user_ns);
 
 	err = mq_init_ns(ns);
-	if (err) {
-		put_user_ns(ns->user_ns);
-		ns_free_inum(&ns->ns);
-		kfree(ns);
-		return ERR_PTR(err);
-	}
+	if (err)
+		goto fail_put;
 	atomic_inc(&nr_ipc_ns);
 
 	sem_init_ns(ns);
@@ -50,6 +59,16 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
 	shm_init_ns(ns);
 
 	return ns;
+
+fail_put:
+	put_user_ns(ns->user_ns);
+	ns_free_inum(&ns->ns);
+fail_free:
+	kfree(ns);
+fail_dec:
+	dec_ipc_namespaces(user_ns);
+fail:
+	return ERR_PTR(err);
 }
 
 struct ipc_namespace *copy_ipcs(unsigned long flags,
@@ -98,6 +117,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)
 	shm_exit_ns(ns);
 	atomic_dec(&nr_ipc_ns);
 
+	dec_ipc_namespaces(ns->user_ns);
 	put_user_ns(ns->user_ns);
 	ns_free_inum(&ns->ns);
 	kfree(ns);
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 6b205c24e888..060d3e099f87 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -79,6 +79,7 @@ static struct ctl_table userns_table[] = {
 	UCOUNT_ENTRY("max_user_namespaces"),
 	UCOUNT_ENTRY("max_pid_namespaces"),
 	UCOUNT_ENTRY("max_uts_namespaces"),
+	UCOUNT_ENTRY("max_ipc_namespaces"),
 	{ }
 };
 #endif /* CONFIG_SYSCTL */
-- 
2.8.3

Re: [PATCH v2 08/10] cgroupns: Add a limit on the number of cgroup namespaces

From: "Serge E. Hallyn" <serge@hallyn.com>
Date: 2016-07-25 23:12:05

Quoting Eric W. Biederman (ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org):
Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
Acked-by: Serge Hallyn <redacted>
quoted hunk
---
 include/linux/user_namespace.h |  1 +
 kernel/cgroup.c                | 15 +++++++++++++++
 kernel/user_namespace.c        |  1 +
 3 files changed, 17 insertions(+)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 367cf08ff63d..1a3a9cb93277 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -27,6 +27,7 @@ enum ucounts {
 	UCOUNT_PID_NAMESPACES,
 	UCOUNT_UTS_NAMESPACES,
 	UCOUNT_IPC_NAMESPACES,
+	UCOUNT_CGROUP_NAMESPACES,
 	UCOUNT_COUNTS,
 };
 
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 86cb5c6e8932..240eec43390b 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -6260,6 +6260,16 @@ void cgroup_sk_free(struct sock_cgroup_data *skcd)
 
 /* cgroup namespaces */
 
+static bool inc_cgroup_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_CGROUP_NAMESPACES);
+}
+
+static void dec_cgroup_namespaces(struct user_namespace *ns)
+{
+	dec_ucount(ns, UCOUNT_CGROUP_NAMESPACES);
+}
+
 static struct cgroup_namespace *alloc_cgroup_ns(void)
 {
 	struct cgroup_namespace *new_ns;
@@ -6281,6 +6291,7 @@ static struct cgroup_namespace *alloc_cgroup_ns(void)
 void free_cgroup_ns(struct cgroup_namespace *ns)
 {
 	put_css_set(ns->root_cset);
+	dec_cgroup_namespaces(ns->user_ns);
 	put_user_ns(ns->user_ns);
 	ns_free_inum(&ns->ns);
 	kfree(ns);
@@ -6305,6 +6316,9 @@ struct cgroup_namespace *copy_cgroup_ns(unsigned long flags,
 	if (!ns_capable(user_ns, CAP_SYS_ADMIN))
 		return ERR_PTR(-EPERM);
 
+	if (!inc_cgroup_namespaces(user_ns))
+		return ERR_PTR(-ENFILE);
+
 	mutex_lock(&cgroup_mutex);
 	spin_lock_bh(&css_set_lock);
 
@@ -6317,6 +6331,7 @@ struct cgroup_namespace *copy_cgroup_ns(unsigned long flags,
 	new_ns = alloc_cgroup_ns();
 	if (IS_ERR(new_ns)) {
 		put_css_set(cset);
+		dec_cgroup_namespaces(user_ns);
 		return new_ns;
 	}
 
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 060d3e099f87..1cf074cb47e2 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -80,6 +80,7 @@ static struct ctl_table userns_table[] = {
 	UCOUNT_ENTRY("max_pid_namespaces"),
 	UCOUNT_ENTRY("max_uts_namespaces"),
 	UCOUNT_ENTRY("max_ipc_namespaces"),
+	UCOUNT_ENTRY("max_cgroup_namespaces"),
 	{ }
 };
 #endif /* CONFIG_SYSCTL */
-- 
2.8.3

Re: [PATCH v2 09/10] netns: Add a limit on the number of net namespaces

From: "Serge E. Hallyn" <serge@hallyn.com>
Date: 2016-07-25 23:13:30

Quoting Eric W. Biederman (ebiederm@xmission.com):
Signed-off-by: "Eric W. Biederman" <redacted>
Acked-by: Serge Hallyn <serge@hallyn.com>
quoted hunk
---
 include/linux/user_namespace.h |  1 +
 kernel/user_namespace.c        |  1 +
 net/core/net_namespace.c       | 15 +++++++++++++++
 3 files changed, 17 insertions(+)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 1a3a9cb93277..f86afa536baf 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -27,6 +27,7 @@ enum ucounts {
 	UCOUNT_PID_NAMESPACES,
 	UCOUNT_UTS_NAMESPACES,
 	UCOUNT_IPC_NAMESPACES,
+	UCOUNT_NET_NAMESPACES,
 	UCOUNT_CGROUP_NAMESPACES,
 	UCOUNT_COUNTS,
 };
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 1cf074cb47e2..e326ca722ae0 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -80,6 +80,7 @@ static struct ctl_table userns_table[] = {
 	UCOUNT_ENTRY("max_pid_namespaces"),
 	UCOUNT_ENTRY("max_uts_namespaces"),
 	UCOUNT_ENTRY("max_ipc_namespaces"),
+	UCOUNT_ENTRY("max_net_namespaces"),
 	UCOUNT_ENTRY("max_cgroup_namespaces"),
 	{ }
 };
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 2c2eb1b629b1..a489f192d619 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -266,6 +266,16 @@ struct net *get_net_ns_by_id(struct net *net, int id)
 	return peer;
 }
 
+static bool inc_net_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_NET_NAMESPACES);
+}
+
+static void dec_net_namespaces(struct user_namespace *ns)
+{
+	dec_ucount(ns, UCOUNT_NET_NAMESPACES);
+}
+
 /*
  * setup_net runs the initializers for the network namespace object.
  */
@@ -276,6 +286,9 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
 	int error = 0;
 	LIST_HEAD(net_exit_list);
 
+	if (!inc_net_namespaces(user_ns))
+		return -ENFILE;
+
 	atomic_set(&net->count, 1);
 	atomic_set(&net->passive, 1);
 	net->dev_base_seq = 1;
@@ -372,6 +385,7 @@ struct net *copy_net_ns(unsigned long flags,
 	}
 	mutex_unlock(&net_mutex);
 	if (rv < 0) {
+		dec_net_namespaces(user_ns);
 		put_user_ns(user_ns);
 		net_drop_ns(net);
 		return ERR_PTR(rv);
@@ -444,6 +458,7 @@ static void cleanup_net(struct work_struct *work)
 	/* Finally it is safe to free my network namespace structure */
 	list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) {
 		list_del_init(&net->exit_list);
+		dec_net_namespaces(net->user_ns);
 		put_user_ns(net->user_ns);
 		net_drop_ns(net);
 	}
-- 
2.8.3

Re: [PATCH v2 10/10] mntns: Add a limit on the number of mount namespaces.

From: "Serge E. Hallyn" <serge@hallyn.com>
Date: 2016-07-25 23:15:25

Quoting Eric W. Biederman (ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org):
Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
Acked-by: Serge Hallyn <redacted>

Thanks, Eric.
quoted hunk
---
 fs/namespace.c                 | 19 ++++++++++++++++++-
 include/linux/user_namespace.h |  1 +
 kernel/user_namespace.c        |  1 +
 3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index aabe8e397fc3..3942ae6c34f5 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2718,9 +2718,20 @@ dput_out:
 	return retval;
 }
 
+static bool inc_mnt_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, UCOUNT_MNT_NAMESPACES);
+}
+
+static void dec_mnt_namespaces(struct user_namespace *ns)
+{
+	dec_ucount(ns, UCOUNT_MNT_NAMESPACES);
+}
+
 static void free_mnt_ns(struct mnt_namespace *ns)
 {
 	ns_free_inum(&ns->ns);
+	dec_mnt_namespaces(ns->user_ns);
 	put_user_ns(ns->user_ns);
 	kfree(ns);
 }
@@ -2739,12 +2750,18 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns)
 	struct mnt_namespace *new_ns;
 	int ret;
 
+	if (!inc_mnt_namespaces(user_ns))
+		return ERR_PTR(-ENFILE);
+
 	new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
-	if (!new_ns)
+	if (!new_ns) {
+		dec_mnt_namespaces(user_ns);
 		return ERR_PTR(-ENOMEM);
+	}
 	ret = ns_alloc_inum(&new_ns->ns);
 	if (ret) {
 		kfree(new_ns);
+		dec_mnt_namespaces(user_ns);
 		return ERR_PTR(ret);
 	}
 	new_ns->ns.ops = &mntns_operations;
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index f86afa536baf..ee0e9d7b2e67 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -28,6 +28,7 @@ enum ucounts {
 	UCOUNT_UTS_NAMESPACES,
 	UCOUNT_IPC_NAMESPACES,
 	UCOUNT_NET_NAMESPACES,
+	UCOUNT_MNT_NAMESPACES,
 	UCOUNT_CGROUP_NAMESPACES,
 	UCOUNT_COUNTS,
 };
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index e326ca722ae0..f6702d84afab 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -81,6 +81,7 @@ static struct ctl_table userns_table[] = {
 	UCOUNT_ENTRY("max_uts_namespaces"),
 	UCOUNT_ENTRY("max_ipc_namespaces"),
 	UCOUNT_ENTRY("max_net_namespaces"),
+	UCOUNT_ENTRY("max_mnt_namespaces"),
 	UCOUNT_ENTRY("max_cgroup_namespaces"),
 	{ }
 };
-- 
2.8.3

Re: [PATCH v2 09/10] netns: Add a limit on the number of net namespaces

From: Andrei Vagin <hidden>
Date: 2016-07-26 06:01:19

On Thu, Jul 21, 2016 at 9:40 AM, Eric W. Biederman
[off-list ref] wrote:
quoted hunk
Signed-off-by: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org>
---
 include/linux/user_namespace.h |  1 +
 kernel/user_namespace.c        |  1 +
 net/core/net_namespace.c       | 15 +++++++++++++++
 3 files changed, 17 insertions(+)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 1a3a9cb93277..f86afa536baf 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -27,6 +27,7 @@ enum ucounts {
        UCOUNT_PID_NAMESPACES,
        UCOUNT_UTS_NAMESPACES,
        UCOUNT_IPC_NAMESPACES,
+       UCOUNT_NET_NAMESPACES,
        UCOUNT_CGROUP_NAMESPACES,
        UCOUNT_COUNTS,
 };
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 1cf074cb47e2..e326ca722ae0 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -80,6 +80,7 @@ static struct ctl_table userns_table[] = {
        UCOUNT_ENTRY("max_pid_namespaces"),
        UCOUNT_ENTRY("max_uts_namespaces"),
        UCOUNT_ENTRY("max_ipc_namespaces"),
+       UCOUNT_ENTRY("max_net_namespaces"),
        UCOUNT_ENTRY("max_cgroup_namespaces"),
        { }
 };
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 2c2eb1b629b1..a489f192d619 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -266,6 +266,16 @@ struct net *get_net_ns_by_id(struct net *net, int id)
        return peer;
 }

+static bool inc_net_namespaces(struct user_namespace *ns)
+{
+       return inc_ucount(ns, UCOUNT_NET_NAMESPACES);
+}
+
+static void dec_net_namespaces(struct user_namespace *ns)
+{
+       dec_ucount(ns, UCOUNT_NET_NAMESPACES);
+}
+
 /*
  * setup_net runs the initializers for the network namespace object.
  */
@@ -276,6 +286,9 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
        int error = 0;
        LIST_HEAD(net_exit_list);

+       if (!inc_net_namespaces(user_ns))
+               return -ENFILE;
I think you need to move this check after initilizing  net->passive.
When setup_net returns an error, net_drop_ns is called:

void net_drop_ns(void *p)
{
        struct net *ns = p;
        if (ns && atomic_dec_and_test(&ns->passive))
                net_free(ns);
}

Actually, I think it would be better to make this check before net_alloc().
quoted hunk
+
        atomic_set(&net->count, 1);
        atomic_set(&net->passive, 1);
        net->dev_base_seq = 1;
@@ -372,6 +385,7 @@ struct net *copy_net_ns(unsigned long flags,
        }
        mutex_unlock(&net_mutex);
        if (rv < 0) {
+               dec_net_namespaces(user_ns);
                put_user_ns(user_ns);
                net_drop_ns(net);
                return ERR_PTR(rv);
@@ -444,6 +458,7 @@ static void cleanup_net(struct work_struct *work)
        /* Finally it is safe to free my network namespace structure */
        list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) {
                list_del_init(&net->exit_list);
+               dec_net_namespaces(net->user_ns);
                put_user_ns(net->user_ns);
                net_drop_ns(net);
        }
--
2.8.3

_______________________________________________
Containers mailing list
Containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
https://lists.linuxfoundation.org/mailman/listinfo/containers

Re: [PATCH v2 00/10] userns: sysctl limits for namespaces

From: Michael Kerrisk (man-pages) <hidden>
Date: 2016-07-26 10:28:06

Hello Eric,

On 07/21/2016 06:39 PM, Eric W. Biederman wrote:
This patchset addresses two use cases:
- Implement a sane upper bound on the number of namespaces.
- Provide a way for sandboxes to limit the attack surface from
  namespaces.

The maximum sane case I can imagine is if every process is a fat
process, so I set the maximum number of namespaces to the maximum
number of threads.

I make these limits recursive and per user namespace so that a
usernamespace root can reduce the limits further.  If a user namespace
root raises the limit the limit in the parent namespace will be honored.

I have cut this implementation to the bare minimum needed to achieve
these objectives.

Does anyone know if there is a proper error code to return for resource
limit exceeded?  I am currently using -EUSERS or -ENFILE but both of
those feel a little wrong.
ENFILE certainly seems weird. I suppose my first question is: why two
different errors?

Some alternatives you might want to consider: E2BIG, EOVERFLOW,
or (maybe) ERANGE.

Cheers,

Michael

Re: [PATCH v2 00/10] userns: sysctl limits for namespaces

From: Michael Kerrisk (man-pages) <hidden>
Date: 2016-07-26 10:30:10

Hello Eric,

I realized I had a question after the last mail.

On 07/21/2016 06:39 PM, Eric W. Biederman wrote:
This patchset addresses two use cases:
- Implement a sane upper bound on the number of namespaces.
- Provide a way for sandboxes to limit the attack surface from
  namespaces.
Can you say more about the second point? What exactly is the
problem that is being addressed, and how does the patch series
address it? (It would be good to have those details in the
revised commit message...)

Cheers,

Michael
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help