diff options
-rw-r--r-- | include/linux/user_namespace.h | 14 | ||||
-rw-r--r-- | kernel/fork.c | 5 | ||||
-rw-r--r-- | kernel/ucount.c | 39 | ||||
-rw-r--r-- | kernel/user_namespace.c | 16 |
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 */ | |||
23 | #define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED | 23 | #define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED |
24 | 24 | ||
25 | struct ucounts; | 25 | struct ucounts; |
26 | |||
27 | enum ucount_type { | ||
28 | UCOUNT_USER_NAMESPACES, | ||
29 | UCOUNT_COUNTS, | ||
30 | }; | ||
31 | |||
26 | struct user_namespace { | 32 | struct user_namespace { |
27 | struct uid_gid_map uid_map; | 33 | struct uid_gid_map uid_map; |
28 | struct uid_gid_map gid_map; | 34 | struct uid_gid_map gid_map; |
@@ -46,7 +52,7 @@ struct user_namespace { | |||
46 | struct ctl_table_header *sysctls; | 52 | struct ctl_table_header *sysctls; |
47 | #endif | 53 | #endif |
48 | struct ucounts *ucounts; | 54 | struct ucounts *ucounts; |
49 | int max_user_namespaces; | 55 | int ucount_max[UCOUNT_COUNTS]; |
50 | }; | 56 | }; |
51 | 57 | ||
52 | struct ucounts { | 58 | struct ucounts { |
@@ -54,15 +60,15 @@ struct ucounts { | |||
54 | struct user_namespace *ns; | 60 | struct user_namespace *ns; |
55 | kuid_t uid; | 61 | kuid_t uid; |
56 | atomic_t count; | 62 | atomic_t count; |
57 | atomic_t user_namespaces; | 63 | atomic_t ucount[UCOUNT_COUNTS]; |
58 | }; | 64 | }; |
59 | 65 | ||
60 | extern struct user_namespace init_user_ns; | 66 | extern struct user_namespace init_user_ns; |
61 | 67 | ||
62 | bool setup_userns_sysctls(struct user_namespace *ns); | 68 | bool setup_userns_sysctls(struct user_namespace *ns); |
63 | void retire_userns_sysctls(struct user_namespace *ns); | 69 | void retire_userns_sysctls(struct user_namespace *ns); |
64 | struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid); | 70 | struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid, enum ucount_type type); |
65 | void dec_user_namespaces(struct ucounts *ucounts); | 71 | void dec_ucount(struct ucounts *ucounts, enum ucount_type type); |
66 | 72 | ||
67 | #ifdef CONFIG_USER_NS | 73 | #ifdef CONFIG_USER_NS |
68 | 74 | ||
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; | |||
302 | 302 | ||
303 | void __init fork_init(void) | 303 | void __init fork_init(void) |
304 | { | 304 | { |
305 | int i; | ||
305 | #ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR | 306 | #ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR |
306 | #ifndef ARCH_MIN_TASKALIGN | 307 | #ifndef ARCH_MIN_TASKALIGN |
307 | #define ARCH_MIN_TASKALIGN L1_CACHE_BYTES | 308 | #define ARCH_MIN_TASKALIGN L1_CACHE_BYTES |
@@ -322,7 +323,9 @@ void __init fork_init(void) | |||
322 | init_task.signal->rlim[RLIMIT_SIGPENDING] = | 323 | init_task.signal->rlim[RLIMIT_SIGPENDING] = |
323 | init_task.signal->rlim[RLIMIT_NPROC]; | 324 | init_task.signal->rlim[RLIMIT_NPROC]; |
324 | 325 | ||
325 | init_user_ns.max_user_namespaces = max_threads/2; | 326 | for (i = 0; i < UCOUNT_COUNTS; i++) { |
327 | init_user_ns.ucount_max[i] = max_threads/2; | ||
328 | } | ||
326 | } | 329 | } |
327 | 330 | ||
328 | int __weak arch_dup_task_struct(struct task_struct *dst, | 331 | 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 = { | |||
57 | 57 | ||
58 | static int zero = 0; | 58 | static int zero = 0; |
59 | static int int_max = INT_MAX; | 59 | static int int_max = INT_MAX; |
60 | #define UCOUNT_ENTRY(name) \ | ||
61 | { \ | ||
62 | .procname = name, \ | ||
63 | .maxlen = sizeof(int), \ | ||
64 | .mode = 0644, \ | ||
65 | .proc_handler = proc_dointvec_minmax, \ | ||
66 | .extra1 = &zero, \ | ||
67 | .extra2 = &int_max, \ | ||
68 | } | ||
60 | static struct ctl_table user_table[] = { | 69 | static struct ctl_table user_table[] = { |
61 | { | 70 | UCOUNT_ENTRY("max_user_namespaces"), |
62 | .procname = "max_user_namespaces", | ||
63 | .data = &init_user_ns.max_user_namespaces, | ||
64 | .maxlen = sizeof(init_user_ns.max_user_namespaces), | ||
65 | .mode = 0644, | ||
66 | .proc_handler = proc_dointvec_minmax, | ||
67 | .extra1 = &zero, | ||
68 | .extra2 = &int_max, | ||
69 | }, | ||
70 | { } | 71 | { } |
71 | }; | 72 | }; |
72 | #endif /* CONFIG_SYSCTL */ | 73 | #endif /* CONFIG_SYSCTL */ |
@@ -78,8 +79,10 @@ bool setup_userns_sysctls(struct user_namespace *ns) | |||
78 | setup_sysctl_set(&ns->set, &set_root, set_is_seen); | 79 | setup_sysctl_set(&ns->set, &set_root, set_is_seen); |
79 | tbl = kmemdup(user_table, sizeof(user_table), GFP_KERNEL); | 80 | tbl = kmemdup(user_table, sizeof(user_table), GFP_KERNEL); |
80 | if (tbl) { | 81 | if (tbl) { |
81 | tbl[0].data = &ns->max_user_namespaces; | 82 | int i; |
82 | 83 | for (i = 0; i < UCOUNT_COUNTS; i++) { | |
84 | tbl[i].data = &ns->ucount_max[i]; | ||
85 | } | ||
83 | ns->sysctls = __register_sysctl_table(&ns->set, "user", tbl); | 86 | ns->sysctls = __register_sysctl_table(&ns->set, "user", tbl); |
84 | } | 87 | } |
85 | if (!ns->sysctls) { | 88 | if (!ns->sysctls) { |
@@ -172,7 +175,8 @@ static inline bool atomic_inc_below(atomic_t *v, int u) | |||
172 | } | 175 | } |
173 | } | 176 | } |
174 | 177 | ||
175 | struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid) | 178 | struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid, |
179 | enum ucount_type type) | ||
176 | { | 180 | { |
177 | struct ucounts *ucounts, *iter, *bad; | 181 | struct ucounts *ucounts, *iter, *bad; |
178 | struct user_namespace *tns; | 182 | struct user_namespace *tns; |
@@ -180,31 +184,30 @@ struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid) | |||
180 | for (iter = ucounts; iter; iter = tns->ucounts) { | 184 | for (iter = ucounts; iter; iter = tns->ucounts) { |
181 | int max; | 185 | int max; |
182 | tns = iter->ns; | 186 | tns = iter->ns; |
183 | max = READ_ONCE(tns->max_user_namespaces); | 187 | max = READ_ONCE(tns->ucount_max[type]); |
184 | if (!atomic_inc_below(&iter->user_namespaces, max)) | 188 | if (!atomic_inc_below(&iter->ucount[type], max)) |
185 | goto fail; | 189 | goto fail; |
186 | } | 190 | } |
187 | return ucounts; | 191 | return ucounts; |
188 | fail: | 192 | fail: |
189 | bad = iter; | 193 | bad = iter; |
190 | for (iter = ucounts; iter != bad; iter = iter->ns->ucounts) | 194 | for (iter = ucounts; iter != bad; iter = iter->ns->ucounts) |
191 | atomic_dec(&iter->user_namespaces); | 195 | atomic_dec(&iter->ucount[type]); |
192 | 196 | ||
193 | put_ucounts(ucounts); | 197 | put_ucounts(ucounts); |
194 | return NULL; | 198 | return NULL; |
195 | } | 199 | } |
196 | 200 | ||
197 | void dec_user_namespaces(struct ucounts *ucounts) | 201 | void dec_ucount(struct ucounts *ucounts, enum ucount_type type) |
198 | { | 202 | { |
199 | struct ucounts *iter; | 203 | struct ucounts *iter; |
200 | for (iter = ucounts; iter; iter = iter->ns->ucounts) { | 204 | for (iter = ucounts; iter; iter = iter->ns->ucounts) { |
201 | int dec = atomic_dec_if_positive(&iter->user_namespaces); | 205 | int dec = atomic_dec_if_positive(&iter->ucount[type]); |
202 | WARN_ON_ONCE(dec < 0); | 206 | WARN_ON_ONCE(dec < 0); |
203 | } | 207 | } |
204 | put_ucounts(ucounts); | 208 | put_ucounts(ucounts); |
205 | } | 209 | } |
206 | 210 | ||
207 | |||
208 | static __init int user_namespace_sysctl_init(void) | 211 | static __init int user_namespace_sysctl_init(void) |
209 | { | 212 | { |
210 | #ifdef CONFIG_SYSCTL | 213 | #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, | |||
31 | struct uid_gid_map *map); | 31 | struct uid_gid_map *map); |
32 | static void free_user_ns(struct work_struct *work); | 32 | static void free_user_ns(struct work_struct *work); |
33 | 33 | ||
34 | static struct ucounts *inc_user_namespaces(struct user_namespace *ns, kuid_t uid) | ||
35 | { | ||
36 | return inc_ucount(ns, uid, UCOUNT_USER_NAMESPACES); | ||
37 | } | ||
38 | |||
39 | static void dec_user_namespaces(struct ucounts *ucounts) | ||
40 | { | ||
41 | return dec_ucount(ucounts, UCOUNT_USER_NAMESPACES); | ||
42 | } | ||
43 | |||
34 | static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns) | 44 | static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns) |
35 | { | 45 | { |
36 | /* Start with the same capabilities as init but useless for doing | 46 | /* Start with the same capabilities as init but useless for doing |
@@ -64,7 +74,7 @@ int create_user_ns(struct cred *new) | |||
64 | kuid_t owner = new->euid; | 74 | kuid_t owner = new->euid; |
65 | kgid_t group = new->egid; | 75 | kgid_t group = new->egid; |
66 | struct ucounts *ucounts; | 76 | struct ucounts *ucounts; |
67 | int ret; | 77 | int ret, i; |
68 | 78 | ||
69 | ret = -EUSERS; | 79 | ret = -EUSERS; |
70 | if (parent_ns->level > 32) | 80 | if (parent_ns->level > 32) |
@@ -110,7 +120,9 @@ int create_user_ns(struct cred *new) | |||
110 | ns->owner = owner; | 120 | ns->owner = owner; |
111 | ns->group = group; | 121 | ns->group = group; |
112 | INIT_WORK(&ns->work, free_user_ns); | 122 | INIT_WORK(&ns->work, free_user_ns); |
113 | ns->max_user_namespaces = INT_MAX; | 123 | for (i = 0; i < UCOUNT_COUNTS; i++) { |
124 | ns->ucount_max[i] = INT_MAX; | ||
125 | } | ||
114 | ns->ucounts = ucounts; | 126 | ns->ucounts = ucounts; |
115 | 127 | ||
116 | /* Inherit USERNS_SETGROUPS_ALLOWED from our parent */ | 128 | /* Inherit USERNS_SETGROUPS_ALLOWED from our parent */ |