netns: Add a limit on the number of net namespaces
authorEric W. Biederman <ebiederm@xmission.com>
Mon, 8 Aug 2016 19:33:23 +0000 (14:33 -0500)
committerEric W. Biederman <ebiederm@xmission.com>
Mon, 8 Aug 2016 19:42:04 +0000 (14:42 -0500)
Acked-by: Kees Cook <keescook@chromium.org>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
include/linux/user_namespace.h
include/net/net_namespace.h
kernel/ucount.c
net/core/net_namespace.c

index d067f0d..c6bc980 100644 (file)
@@ -29,6 +29,7 @@ enum ucount_type {
        UCOUNT_PID_NAMESPACES,
        UCOUNT_UTS_NAMESPACES,
        UCOUNT_IPC_NAMESPACES,
+       UCOUNT_NET_NAMESPACES,
        UCOUNT_CGROUP_NAMESPACES,
        UCOUNT_COUNTS,
 };
index 0933c74..fc4f757 100644 (file)
@@ -60,6 +60,7 @@ struct net {
        struct list_head        exit_list;      /* Use only net_mutex */
 
        struct user_namespace   *user_ns;       /* Owning user namespace */
+       struct ucounts          *ucounts;
        spinlock_t              nsid_lock;
        struct idr              netns_ids;
 
index 335cc5d..205f1a0 100644 (file)
@@ -71,6 +71,7 @@ static struct ctl_table user_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"),
        { }
 };
index 2c2eb1b..3e2812a 100644 (file)
@@ -266,6 +266,16 @@ struct net *get_net_ns_by_id(struct net *net, int id)
        return peer;
 }
 
+static struct ucounts *inc_net_namespaces(struct user_namespace *ns)
+{
+       return inc_ucount(ns, current_euid(), UCOUNT_NET_NAMESPACES);
+}
+
+static void dec_net_namespaces(struct ucounts *ucounts)
+{
+       dec_ucount(ucounts, UCOUNT_NET_NAMESPACES);
+}
+
 /*
  * setup_net runs the initializers for the network namespace object.
  */
@@ -351,19 +361,27 @@ void net_drop_ns(void *p)
 struct net *copy_net_ns(unsigned long flags,
                        struct user_namespace *user_ns, struct net *old_net)
 {
+       struct ucounts *ucounts;
        struct net *net;
        int rv;
 
        if (!(flags & CLONE_NEWNET))
                return get_net(old_net);
 
+       ucounts = inc_net_namespaces(user_ns);
+       if (!ucounts)
+               return ERR_PTR(-ENFILE);
+
        net = net_alloc();
-       if (!net)
+       if (!net) {
+               dec_net_namespaces(ucounts);
                return ERR_PTR(-ENOMEM);
+       }
 
        get_user_ns(user_ns);
 
        mutex_lock(&net_mutex);
+       net->ucounts = ucounts;
        rv = setup_net(net, user_ns);
        if (rv == 0) {
                rtnl_lock();
@@ -372,6 +390,7 @@ struct net *copy_net_ns(unsigned long flags,
        }
        mutex_unlock(&net_mutex);
        if (rv < 0) {
+               dec_net_namespaces(ucounts);
                put_user_ns(user_ns);
                net_drop_ns(net);
                return ERR_PTR(rv);
@@ -444,6 +463,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->ucounts);
                put_user_ns(net->user_ns);
                net_drop_ns(net);
        }