diff options
-rw-r--r-- | fs/ioprio.c | 18 | ||||
-rw-r--r-- | include/linux/sched.h | 8 | ||||
-rw-r--r-- | include/linux/user_namespace.h | 4 | ||||
-rw-r--r-- | kernel/sys.c | 34 | ||||
-rw-r--r-- | kernel/user.c | 28 | ||||
-rw-r--r-- | kernel/user_namespace.c | 6 |
6 files changed, 55 insertions, 43 deletions
diff --git a/fs/ioprio.c b/fs/ioprio.c index 0f1b9515213b..8e35e964d9ed 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c | |||
@@ -65,6 +65,7 @@ SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio) | |||
65 | struct task_struct *p, *g; | 65 | struct task_struct *p, *g; |
66 | struct user_struct *user; | 66 | struct user_struct *user; |
67 | struct pid *pgrp; | 67 | struct pid *pgrp; |
68 | kuid_t uid; | ||
68 | int ret; | 69 | int ret; |
69 | 70 | ||
70 | switch (class) { | 71 | switch (class) { |
@@ -110,16 +111,21 @@ SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio) | |||
110 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); | 111 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); |
111 | break; | 112 | break; |
112 | case IOPRIO_WHO_USER: | 113 | case IOPRIO_WHO_USER: |
114 | uid = make_kuid(current_user_ns(), who); | ||
115 | if (!uid_valid(uid)) | ||
116 | break; | ||
113 | if (!who) | 117 | if (!who) |
114 | user = current_user(); | 118 | user = current_user(); |
115 | else | 119 | else |
116 | user = find_user(who); | 120 | user = find_user(uid); |
117 | 121 | ||
118 | if (!user) | 122 | if (!user) |
119 | break; | 123 | break; |
120 | 124 | ||
121 | do_each_thread(g, p) { | 125 | do_each_thread(g, p) { |
122 | if (__task_cred(p)->uid != who) | 126 | const struct cred *tcred = __task_cred(p); |
127 | kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); | ||
128 | if (!uid_eq(tcred_uid, uid)) | ||
123 | continue; | 129 | continue; |
124 | ret = set_task_ioprio(p, ioprio); | 130 | ret = set_task_ioprio(p, ioprio); |
125 | if (ret) | 131 | if (ret) |
@@ -174,6 +180,7 @@ SYSCALL_DEFINE2(ioprio_get, int, which, int, who) | |||
174 | struct task_struct *g, *p; | 180 | struct task_struct *g, *p; |
175 | struct user_struct *user; | 181 | struct user_struct *user; |
176 | struct pid *pgrp; | 182 | struct pid *pgrp; |
183 | kuid_t uid; | ||
177 | int ret = -ESRCH; | 184 | int ret = -ESRCH; |
178 | int tmpio; | 185 | int tmpio; |
179 | 186 | ||
@@ -203,16 +210,19 @@ SYSCALL_DEFINE2(ioprio_get, int, which, int, who) | |||
203 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); | 210 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); |
204 | break; | 211 | break; |
205 | case IOPRIO_WHO_USER: | 212 | case IOPRIO_WHO_USER: |
213 | uid = make_kuid(current_user_ns(), who); | ||
206 | if (!who) | 214 | if (!who) |
207 | user = current_user(); | 215 | user = current_user(); |
208 | else | 216 | else |
209 | user = find_user(who); | 217 | user = find_user(uid); |
210 | 218 | ||
211 | if (!user) | 219 | if (!user) |
212 | break; | 220 | break; |
213 | 221 | ||
214 | do_each_thread(g, p) { | 222 | do_each_thread(g, p) { |
215 | if (__task_cred(p)->uid != user->uid) | 223 | const struct cred *tcred = __task_cred(p); |
224 | kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); | ||
225 | if (!uid_eq(tcred_uid, user->uid)) | ||
216 | continue; | 226 | continue; |
217 | tmpio = get_task_ioprio(p); | 227 | tmpio = get_task_ioprio(p); |
218 | if (tmpio < 0) | 228 | if (tmpio < 0) |
diff --git a/include/linux/sched.h b/include/linux/sched.h index 6867ae9bc8a0..5fdc1ebbcbc4 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -90,6 +90,7 @@ struct sched_param { | |||
90 | #include <linux/latencytop.h> | 90 | #include <linux/latencytop.h> |
91 | #include <linux/cred.h> | 91 | #include <linux/cred.h> |
92 | #include <linux/llist.h> | 92 | #include <linux/llist.h> |
93 | #include <linux/uidgid.h> | ||
93 | 94 | ||
94 | #include <asm/processor.h> | 95 | #include <asm/processor.h> |
95 | 96 | ||
@@ -728,8 +729,7 @@ struct user_struct { | |||
728 | 729 | ||
729 | /* Hash table maintenance information */ | 730 | /* Hash table maintenance information */ |
730 | struct hlist_node uidhash_node; | 731 | struct hlist_node uidhash_node; |
731 | uid_t uid; | 732 | kuid_t uid; |
732 | struct user_namespace *_user_ns; /* Don't use will be removed soon */ | ||
733 | 733 | ||
734 | #ifdef CONFIG_PERF_EVENTS | 734 | #ifdef CONFIG_PERF_EVENTS |
735 | atomic_long_t locked_vm; | 735 | atomic_long_t locked_vm; |
@@ -738,7 +738,7 @@ struct user_struct { | |||
738 | 738 | ||
739 | extern int uids_sysfs_init(void); | 739 | extern int uids_sysfs_init(void); |
740 | 740 | ||
741 | extern struct user_struct *find_user(uid_t); | 741 | extern struct user_struct *find_user(kuid_t); |
742 | 742 | ||
743 | extern struct user_struct root_user; | 743 | extern struct user_struct root_user; |
744 | #define INIT_USER (&root_user) | 744 | #define INIT_USER (&root_user) |
@@ -2177,7 +2177,7 @@ extern struct task_struct *find_task_by_pid_ns(pid_t nr, | |||
2177 | extern void __set_special_pids(struct pid *pid); | 2177 | extern void __set_special_pids(struct pid *pid); |
2178 | 2178 | ||
2179 | /* per-UID process charging. */ | 2179 | /* per-UID process charging. */ |
2180 | extern struct user_struct * alloc_uid(struct user_namespace *, uid_t); | 2180 | extern struct user_struct * alloc_uid(kuid_t); |
2181 | static inline struct user_struct *get_uid(struct user_struct *u) | 2181 | static inline struct user_struct *get_uid(struct user_struct *u) |
2182 | { | 2182 | { |
2183 | atomic_inc(&u->__count); | 2183 | atomic_inc(&u->__count); |
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index dc2d85a76376..d767508db4f9 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h | |||
@@ -6,12 +6,8 @@ | |||
6 | #include <linux/sched.h> | 6 | #include <linux/sched.h> |
7 | #include <linux/err.h> | 7 | #include <linux/err.h> |
8 | 8 | ||
9 | #define UIDHASH_BITS (CONFIG_BASE_SMALL ? 3 : 7) | ||
10 | #define UIDHASH_SZ (1 << UIDHASH_BITS) | ||
11 | |||
12 | struct user_namespace { | 9 | struct user_namespace { |
13 | struct kref kref; | 10 | struct kref kref; |
14 | struct hlist_head uidhash_table[UIDHASH_SZ]; | ||
15 | struct user_namespace *parent; | 11 | struct user_namespace *parent; |
16 | struct user_struct *creator; | 12 | struct user_struct *creator; |
17 | struct work_struct destroyer; | 13 | struct work_struct destroyer; |
diff --git a/kernel/sys.c b/kernel/sys.c index 71852417cfc8..f0c43b4b6657 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -175,6 +175,8 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval) | |||
175 | const struct cred *cred = current_cred(); | 175 | const struct cred *cred = current_cred(); |
176 | int error = -EINVAL; | 176 | int error = -EINVAL; |
177 | struct pid *pgrp; | 177 | struct pid *pgrp; |
178 | kuid_t cred_uid; | ||
179 | kuid_t uid; | ||
178 | 180 | ||
179 | if (which > PRIO_USER || which < PRIO_PROCESS) | 181 | if (which > PRIO_USER || which < PRIO_PROCESS) |
180 | goto out; | 182 | goto out; |
@@ -207,18 +209,22 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval) | |||
207 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); | 209 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); |
208 | break; | 210 | break; |
209 | case PRIO_USER: | 211 | case PRIO_USER: |
212 | cred_uid = make_kuid(cred->user_ns, cred->uid); | ||
213 | uid = make_kuid(cred->user_ns, who); | ||
210 | user = cred->user; | 214 | user = cred->user; |
211 | if (!who) | 215 | if (!who) |
212 | who = cred->uid; | 216 | uid = cred_uid; |
213 | else if ((who != cred->uid) && | 217 | else if (!uid_eq(uid, cred_uid) && |
214 | !(user = find_user(who))) | 218 | !(user = find_user(uid))) |
215 | goto out_unlock; /* No processes for this user */ | 219 | goto out_unlock; /* No processes for this user */ |
216 | 220 | ||
217 | do_each_thread(g, p) { | 221 | do_each_thread(g, p) { |
218 | if (__task_cred(p)->uid == who) | 222 | const struct cred *tcred = __task_cred(p); |
223 | kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); | ||
224 | if (uid_eq(tcred_uid, uid)) | ||
219 | error = set_one_prio(p, niceval, error); | 225 | error = set_one_prio(p, niceval, error); |
220 | } while_each_thread(g, p); | 226 | } while_each_thread(g, p); |
221 | if (who != cred->uid) | 227 | if (!uid_eq(uid, cred_uid)) |
222 | free_uid(user); /* For find_user() */ | 228 | free_uid(user); /* For find_user() */ |
223 | break; | 229 | break; |
224 | } | 230 | } |
@@ -242,6 +248,8 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who) | |||
242 | const struct cred *cred = current_cred(); | 248 | const struct cred *cred = current_cred(); |
243 | long niceval, retval = -ESRCH; | 249 | long niceval, retval = -ESRCH; |
244 | struct pid *pgrp; | 250 | struct pid *pgrp; |
251 | kuid_t cred_uid; | ||
252 | kuid_t uid; | ||
245 | 253 | ||
246 | if (which > PRIO_USER || which < PRIO_PROCESS) | 254 | if (which > PRIO_USER || which < PRIO_PROCESS) |
247 | return -EINVAL; | 255 | return -EINVAL; |
@@ -272,21 +280,25 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who) | |||
272 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); | 280 | } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); |
273 | break; | 281 | break; |
274 | case PRIO_USER: | 282 | case PRIO_USER: |
283 | cred_uid = make_kuid(cred->user_ns, cred->uid); | ||
284 | uid = make_kuid(cred->user_ns, who); | ||
275 | user = cred->user; | 285 | user = cred->user; |
276 | if (!who) | 286 | if (!who) |
277 | who = cred->uid; | 287 | uid = cred_uid; |
278 | else if ((who != cred->uid) && | 288 | else if (!uid_eq(uid, cred_uid) && |
279 | !(user = find_user(who))) | 289 | !(user = find_user(uid))) |
280 | goto out_unlock; /* No processes for this user */ | 290 | goto out_unlock; /* No processes for this user */ |
281 | 291 | ||
282 | do_each_thread(g, p) { | 292 | do_each_thread(g, p) { |
283 | if (__task_cred(p)->uid == who) { | 293 | const struct cred *tcred = __task_cred(p); |
294 | kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); | ||
295 | if (uid_eq(tcred_uid, uid)) { | ||
284 | niceval = 20 - task_nice(p); | 296 | niceval = 20 - task_nice(p); |
285 | if (niceval > retval) | 297 | if (niceval > retval) |
286 | retval = niceval; | 298 | retval = niceval; |
287 | } | 299 | } |
288 | } while_each_thread(g, p); | 300 | } while_each_thread(g, p); |
289 | if (who != cred->uid) | 301 | if (!uid_eq(uid, cred_uid)) |
290 | free_uid(user); /* for find_user() */ | 302 | free_uid(user); /* for find_user() */ |
291 | break; | 303 | break; |
292 | } | 304 | } |
@@ -629,7 +641,7 @@ static int set_user(struct cred *new) | |||
629 | { | 641 | { |
630 | struct user_struct *new_user; | 642 | struct user_struct *new_user; |
631 | 643 | ||
632 | new_user = alloc_uid(current_user_ns(), new->uid); | 644 | new_user = alloc_uid(make_kuid(new->user_ns, new->uid)); |
633 | if (!new_user) | 645 | if (!new_user) |
634 | return -EAGAIN; | 646 | return -EAGAIN; |
635 | 647 | ||
diff --git a/kernel/user.c b/kernel/user.c index d65fec0615a0..025077e54a7c 100644 --- a/kernel/user.c +++ b/kernel/user.c | |||
@@ -34,11 +34,14 @@ EXPORT_SYMBOL_GPL(init_user_ns); | |||
34 | * when changing user ID's (ie setuid() and friends). | 34 | * when changing user ID's (ie setuid() and friends). |
35 | */ | 35 | */ |
36 | 36 | ||
37 | #define UIDHASH_BITS (CONFIG_BASE_SMALL ? 3 : 7) | ||
38 | #define UIDHASH_SZ (1 << UIDHASH_BITS) | ||
37 | #define UIDHASH_MASK (UIDHASH_SZ - 1) | 39 | #define UIDHASH_MASK (UIDHASH_SZ - 1) |
38 | #define __uidhashfn(uid) (((uid >> UIDHASH_BITS) + uid) & UIDHASH_MASK) | 40 | #define __uidhashfn(uid) (((uid >> UIDHASH_BITS) + uid) & UIDHASH_MASK) |
39 | #define uidhashentry(ns, uid) ((ns)->uidhash_table + __uidhashfn((uid))) | 41 | #define uidhashentry(uid) (uidhash_table + __uidhashfn((__kuid_val(uid)))) |
40 | 42 | ||
41 | static struct kmem_cache *uid_cachep; | 43 | static struct kmem_cache *uid_cachep; |
44 | struct hlist_head uidhash_table[UIDHASH_SZ]; | ||
42 | 45 | ||
43 | /* | 46 | /* |
44 | * The uidhash_lock is mostly taken from process context, but it is | 47 | * The uidhash_lock is mostly taken from process context, but it is |
@@ -58,7 +61,7 @@ struct user_struct root_user = { | |||
58 | .files = ATOMIC_INIT(0), | 61 | .files = ATOMIC_INIT(0), |
59 | .sigpending = ATOMIC_INIT(0), | 62 | .sigpending = ATOMIC_INIT(0), |
60 | .locked_shm = 0, | 63 | .locked_shm = 0, |
61 | ._user_ns = &init_user_ns, | 64 | .uid = GLOBAL_ROOT_UID, |
62 | }; | 65 | }; |
63 | 66 | ||
64 | /* | 67 | /* |
@@ -72,16 +75,15 @@ static void uid_hash_insert(struct user_struct *up, struct hlist_head *hashent) | |||
72 | static void uid_hash_remove(struct user_struct *up) | 75 | static void uid_hash_remove(struct user_struct *up) |
73 | { | 76 | { |
74 | hlist_del_init(&up->uidhash_node); | 77 | hlist_del_init(&up->uidhash_node); |
75 | put_user_ns(up->_user_ns); /* It is safe to free the uid hash table now */ | ||
76 | } | 78 | } |
77 | 79 | ||
78 | static struct user_struct *uid_hash_find(uid_t uid, struct hlist_head *hashent) | 80 | static struct user_struct *uid_hash_find(kuid_t uid, struct hlist_head *hashent) |
79 | { | 81 | { |
80 | struct user_struct *user; | 82 | struct user_struct *user; |
81 | struct hlist_node *h; | 83 | struct hlist_node *h; |
82 | 84 | ||
83 | hlist_for_each_entry(user, h, hashent, uidhash_node) { | 85 | hlist_for_each_entry(user, h, hashent, uidhash_node) { |
84 | if (user->uid == uid) { | 86 | if (uid_eq(user->uid, uid)) { |
85 | atomic_inc(&user->__count); | 87 | atomic_inc(&user->__count); |
86 | return user; | 88 | return user; |
87 | } | 89 | } |
@@ -110,14 +112,13 @@ static void free_user(struct user_struct *up, unsigned long flags) | |||
110 | * | 112 | * |
111 | * If the user_struct could not be found, return NULL. | 113 | * If the user_struct could not be found, return NULL. |
112 | */ | 114 | */ |
113 | struct user_struct *find_user(uid_t uid) | 115 | struct user_struct *find_user(kuid_t uid) |
114 | { | 116 | { |
115 | struct user_struct *ret; | 117 | struct user_struct *ret; |
116 | unsigned long flags; | 118 | unsigned long flags; |
117 | struct user_namespace *ns = current_user_ns(); | ||
118 | 119 | ||
119 | spin_lock_irqsave(&uidhash_lock, flags); | 120 | spin_lock_irqsave(&uidhash_lock, flags); |
120 | ret = uid_hash_find(uid, uidhashentry(ns, uid)); | 121 | ret = uid_hash_find(uid, uidhashentry(uid)); |
121 | spin_unlock_irqrestore(&uidhash_lock, flags); | 122 | spin_unlock_irqrestore(&uidhash_lock, flags); |
122 | return ret; | 123 | return ret; |
123 | } | 124 | } |
@@ -136,9 +137,9 @@ void free_uid(struct user_struct *up) | |||
136 | local_irq_restore(flags); | 137 | local_irq_restore(flags); |
137 | } | 138 | } |
138 | 139 | ||
139 | struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid) | 140 | struct user_struct *alloc_uid(kuid_t uid) |
140 | { | 141 | { |
141 | struct hlist_head *hashent = uidhashentry(ns, uid); | 142 | struct hlist_head *hashent = uidhashentry(uid); |
142 | struct user_struct *up, *new; | 143 | struct user_struct *up, *new; |
143 | 144 | ||
144 | spin_lock_irq(&uidhash_lock); | 145 | spin_lock_irq(&uidhash_lock); |
@@ -153,8 +154,6 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid) | |||
153 | new->uid = uid; | 154 | new->uid = uid; |
154 | atomic_set(&new->__count, 1); | 155 | atomic_set(&new->__count, 1); |
155 | 156 | ||
156 | new->_user_ns = get_user_ns(ns); | ||
157 | |||
158 | /* | 157 | /* |
159 | * Before adding this, check whether we raced | 158 | * Before adding this, check whether we raced |
160 | * on adding the same user already.. | 159 | * on adding the same user already.. |
@@ -162,7 +161,6 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid) | |||
162 | spin_lock_irq(&uidhash_lock); | 161 | spin_lock_irq(&uidhash_lock); |
163 | up = uid_hash_find(uid, hashent); | 162 | up = uid_hash_find(uid, hashent); |
164 | if (up) { | 163 | if (up) { |
165 | put_user_ns(ns); | ||
166 | key_put(new->uid_keyring); | 164 | key_put(new->uid_keyring); |
167 | key_put(new->session_keyring); | 165 | key_put(new->session_keyring); |
168 | kmem_cache_free(uid_cachep, new); | 166 | kmem_cache_free(uid_cachep, new); |
@@ -187,11 +185,11 @@ static int __init uid_cache_init(void) | |||
187 | 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); | 185 | 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); |
188 | 186 | ||
189 | for(n = 0; n < UIDHASH_SZ; ++n) | 187 | for(n = 0; n < UIDHASH_SZ; ++n) |
190 | INIT_HLIST_HEAD(init_user_ns.uidhash_table + n); | 188 | INIT_HLIST_HEAD(uidhash_table + n); |
191 | 189 | ||
192 | /* Insert the root user immediately (init already runs as root) */ | 190 | /* Insert the root user immediately (init already runs as root) */ |
193 | spin_lock_irq(&uidhash_lock); | 191 | spin_lock_irq(&uidhash_lock); |
194 | uid_hash_insert(&root_user, uidhashentry(&init_user_ns, 0)); | 192 | uid_hash_insert(&root_user, uidhashentry(GLOBAL_ROOT_UID)); |
195 | spin_unlock_irq(&uidhash_lock); | 193 | spin_unlock_irq(&uidhash_lock); |
196 | 194 | ||
197 | return 0; | 195 | return 0; |
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index e216e1e8ce84..898e973bd1e8 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c | |||
@@ -27,7 +27,6 @@ int create_user_ns(struct cred *new) | |||
27 | { | 27 | { |
28 | struct user_namespace *ns, *parent_ns = new->user_ns; | 28 | struct user_namespace *ns, *parent_ns = new->user_ns; |
29 | struct user_struct *root_user; | 29 | struct user_struct *root_user; |
30 | int n; | ||
31 | 30 | ||
32 | ns = kmem_cache_alloc(user_ns_cachep, GFP_KERNEL); | 31 | ns = kmem_cache_alloc(user_ns_cachep, GFP_KERNEL); |
33 | if (!ns) | 32 | if (!ns) |
@@ -35,11 +34,8 @@ int create_user_ns(struct cred *new) | |||
35 | 34 | ||
36 | kref_init(&ns->kref); | 35 | kref_init(&ns->kref); |
37 | 36 | ||
38 | for (n = 0; n < UIDHASH_SZ; ++n) | ||
39 | INIT_HLIST_HEAD(ns->uidhash_table + n); | ||
40 | |||
41 | /* Alloc new root user. */ | 37 | /* Alloc new root user. */ |
42 | root_user = alloc_uid(ns, 0); | 38 | root_user = alloc_uid(make_kuid(ns, 0)); |
43 | if (!root_user) { | 39 | if (!root_user) { |
44 | kmem_cache_free(user_ns_cachep, ns); | 40 | kmem_cache_free(user_ns_cachep, ns); |
45 | return -ENOMEM; | 41 | return -ENOMEM; |