summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/user_namespace.h14
-rw-r--r--kernel/fork.c5
-rw-r--r--kernel/ucount.c39
-rw-r--r--kernel/user_namespace.c16
4 files changed, 49 insertions, 25 deletions
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 826de7a12a20..9b676ead35c3 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -23,6 +23,12 @@ struct uid_gid_map { /* 64 bytes -- 1 cache line */
#define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED
struct ucounts;
+
+enum ucount_type {
+ UCOUNT_USER_NAMESPACES,
+ UCOUNT_COUNTS,
+};
+
struct user_namespace {
struct uid_gid_map uid_map;
struct uid_gid_map gid_map;
@@ -46,7 +52,7 @@ struct user_namespace {
struct ctl_table_header *sysctls;
#endif
struct ucounts *ucounts;
- int max_user_namespaces;
+ int ucount_max[UCOUNT_COUNTS];
};
struct ucounts {
@@ -54,15 +60,15 @@ struct ucounts {
struct user_namespace *ns;
kuid_t uid;
atomic_t count;
- atomic_t user_namespaces;
+ atomic_t ucount[UCOUNT_COUNTS];
};
extern struct user_namespace init_user_ns;
bool setup_userns_sysctls(struct user_namespace *ns);
void retire_userns_sysctls(struct user_namespace *ns);
-struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid);
-void dec_user_namespaces(struct ucounts *ucounts);
+struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid, enum ucount_type type);
+void dec_ucount(struct ucounts *ucounts, enum ucount_type type);
#ifdef CONFIG_USER_NS
diff --git a/kernel/fork.c b/kernel/fork.c
index d8cde533ace3..3cb4853a59aa 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -302,6 +302,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
@@ -322,7 +323,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/2;
+ for (i = 0; i < UCOUNT_COUNTS; i++) {
+ init_user_ns.ucount_max[i] = max_threads/2;
+ }
}
int __weak arch_dup_task_struct(struct task_struct *dst,
diff --git a/kernel/ucount.c b/kernel/ucount.c
index 33c418718304..0f9ab3b26185 100644
--- a/kernel/ucount.c
+++ b/kernel/ucount.c
@@ -57,16 +57,17 @@ static struct ctl_table_root set_root = {
static int zero = 0;
static int int_max = INT_MAX;
+#define UCOUNT_ENTRY(name) \
+ { \
+ .procname = name, \
+ .maxlen = sizeof(int), \
+ .mode = 0644, \
+ .proc_handler = proc_dointvec_minmax, \
+ .extra1 = &zero, \
+ .extra2 = &int_max, \
+ }
static struct ctl_table user_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 = &int_max,
- },
+ UCOUNT_ENTRY("max_user_namespaces"),
{ }
};
#endif /* CONFIG_SYSCTL */
@@ -78,8 +79,10 @@ bool setup_userns_sysctls(struct user_namespace *ns)
setup_sysctl_set(&ns->set, &set_root, set_is_seen);
tbl = kmemdup(user_table, sizeof(user_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_max[i];
+ }
ns->sysctls = __register_sysctl_table(&ns->set, "user", tbl);
}
if (!ns->sysctls) {
@@ -172,7 +175,8 @@ static inline bool atomic_inc_below(atomic_t *v, int u)
}
}
-struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid)
+struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid,
+ enum ucount_type type)
{
struct ucounts *ucounts, *iter, *bad;
struct user_namespace *tns;
@@ -180,31 +184,30 @@ struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid)
for (iter = ucounts; iter; iter = tns->ucounts) {
int max;
tns = iter->ns;
- max = READ_ONCE(tns->max_user_namespaces);
- if (!atomic_inc_below(&iter->user_namespaces, max))
+ max = READ_ONCE(tns->ucount_max[type]);
+ if (!atomic_inc_below(&iter->ucount[type], max))
goto fail;
}
return ucounts;
fail:
bad = iter;
for (iter = ucounts; iter != bad; iter = iter->ns->ucounts)
- atomic_dec(&iter->user_namespaces);
+ atomic_dec(&iter->ucount[type]);
put_ucounts(ucounts);
return NULL;
}
-void dec_user_namespaces(struct ucounts *ucounts)
+void dec_ucount(struct ucounts *ucounts, enum ucount_type type)
{
struct ucounts *iter;
for (iter = ucounts; iter; iter = iter->ns->ucounts) {
- int dec = atomic_dec_if_positive(&iter->user_namespaces);
+ int dec = atomic_dec_if_positive(&iter->ucount[type]);
WARN_ON_ONCE(dec < 0);
}
put_ucounts(ucounts);
}
-
static __init int user_namespace_sysctl_init(void)
{
#ifdef CONFIG_SYSCTL
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 58c67e5f851c..0edafe305861 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -31,6 +31,16 @@ static bool new_idmap_permitted(const struct file *file,
struct uid_gid_map *map);
static void free_user_ns(struct work_struct *work);
+static struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid)
+{
+ return inc_ucount(ns, uid, UCOUNT_USER_NAMESPACES);
+}
+
+static void dec_user_namespaces(struct ucounts *ucounts)
+{
+ return dec_ucount(ucounts, UCOUNT_USER_NAMESPACES);
+}
+
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
@@ -64,7 +74,7 @@ int create_user_ns(struct cred *new)
kuid_t owner = new->euid;
kgid_t group = new->egid;
struct ucounts *ucounts;
- int ret;
+ int ret, i;
ret = -EUSERS;
if (parent_ns->level > 32)
@@ -110,7 +120,9 @@ int create_user_ns(struct cred *new)
ns->owner = owner;
ns->group = group;
INIT_WORK(&ns->work, free_user_ns);
- ns->max_user_namespaces = INT_MAX;
+ for (i = 0; i < UCOUNT_COUNTS; i++) {
+ ns->ucount_max[i] = INT_MAX;
+ }
ns->ucounts = ucounts;
/* Inherit USERNS_SETGROUPS_ALLOWED from our parent */