aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Brauner <christian.brauner@ubuntu.com>2017-10-24 18:04:41 -0400
committerEric W. Biederman <ebiederm@xmission.com>2017-10-31 18:23:04 -0400
commit6397fac4915ab3002dc15aae751455da1a852f25 (patch)
tree750dfb311ae12d69914d15508044fca9c90cc462
parentaa4bf44dc851c6bdd4f7b61b5f2c56c84dfe2ff0 (diff)
userns: bump idmap limits to 340
There are quite some use cases where users run into the current limit for {g,u}id mappings. Consider a user requesting us to map everything but 999, and 1001 for a given range of 1000000000 with a sub{g,u}id layout of: some-user:100000:1000000000 some-user:999:1 some-user:1000:1 some-user:1001:1 some-user:1002:1 This translates to: MAPPING-TYPE | CONTAINER | HOST | RANGE | -------------|-----------|---------|-----------| uid | 999 | 999 | 1 | uid | 1001 | 1001 | 1 | uid | 0 | 1000000 | 999 | uid | 1000 | 1001000 | 1 | uid | 1002 | 1001002 | 999998998 | ------------------------------------------------ gid | 999 | 999 | 1 | gid | 1001 | 1001 | 1 | gid | 0 | 1000000 | 999 | gid | 1000 | 1001000 | 1 | gid | 1002 | 1001002 | 999998998 | which is already the current limit. As discussed at LPC simply bumping the number of limits is not going to work since this would mean that struct uid_gid_map won't fit into a single cache-line anymore thereby regressing performance for the base-cases. The same problem seems to arise when using a single pointer. So the idea is to use struct uid_gid_extent { u32 first; u32 lower_first; u32 count; }; struct uid_gid_map { /* 64 bytes -- 1 cache line */ u32 nr_extents; union { struct uid_gid_extent extent[UID_GID_MAP_MAX_BASE_EXTENTS]; struct { struct uid_gid_extent *forward; struct uid_gid_extent *reverse; }; }; }; For the base cases we will only use the struct uid_gid_extent extent member. If we go over UID_GID_MAP_MAX_BASE_EXTENTS mappings we perform a single 4k kmalloc() which means we can have a maximum of 340 mappings (340 * size(struct uid_gid_extent) = 4080). For the latter case we use two pointers "forward" and "reverse". The forward pointer points to an array sorted by "first" and the reverse pointer points to an array sorted by "lower_first". We can then perform binary search on those arrays. Performance Testing: When Eric introduced the extent-based struct uid_gid_map approach he measured the performanc impact of his idmap changes: > My benchmark consisted of going to single user mode where nothing else was > running. On an ext4 filesystem opening 1,000,000 files and looping through all > of the files 1000 times and calling fstat on the individuals files. This was > to ensure I was benchmarking stat times where the inodes were in the kernels > cache, but the inode values were not in the processors cache. My results: > v3.4-rc1: ~= 156ns (unmodified v3.4-rc1 with user namespace support disabled) > v3.4-rc1-userns-: ~= 155ns (v3.4-rc1 with my user namespace patches and user namespace support disabled) > v3.4-rc1-userns+: ~= 164ns (v3.4-rc1 with my user namespace patches and user namespace support enabled) I used an identical approach on my laptop. Here's a thorough description of what I did. I built a 4.14.0-rc4 mainline kernel with my new idmap patches applied. I booted into single user mode and used an ext4 filesystem to open/create 1,000,000 files. Then I looped through all of the files calling fstat() on each of them 1000 times and calculated the mean fstat() time for a single file. (The test program can be found below.) Here are the results. For fun, I compared the first version of my patch which scaled linearly with the new version of the patch: | # MAPPINGS | PATCH-V1 | PATCH-NEW | |--------------|------------|-----------| | 0 mappings | 158 ns | 158 ns | | 1 mappings | 164 ns | 157 ns | | 2 mappings | 170 ns | 158 ns | | 3 mappings | 175 ns | 161 ns | | 5 mappings | 187 ns | 165 ns | | 10 mappings | 218 ns | 199 ns | | 50 mappings | 528 ns | 218 ns | | 100 mappings | 980 ns | 229 ns | | 200 mappings | 1880 ns | 239 ns | | 300 mappings | 2760 ns | 240 ns | | 340 mappings | not tested | 248 ns | Here's the test program I used. I asked Eric what he did and this is a more "advanced" implementation of the idea. It's pretty straight-forward: #define __GNU_SOURCE #define __STDC_FORMAT_MACROS #include <errno.h> #include <dirent.h> #include <fcntl.h> #include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> int main(int argc, char *argv[]) { int ret; size_t i, k; int fd[1000000]; int times[1000]; char pathname[4096]; struct stat st; struct timeval t1, t2; uint64_t time_in_mcs; uint64_t sum = 0; if (argc != 2) { fprintf(stderr, "Please specify a directory where to create " "the test files\n"); exit(EXIT_FAILURE); } for (i = 0; i < sizeof(fd) / sizeof(fd[0]); i++) { sprintf(pathname, "%s/idmap_test_%zu", argv[1], i); fd[i]= open(pathname, O_RDWR | O_CREAT, S_IXUSR | S_IXGRP | S_IXOTH); if (fd[i] < 0) { ssize_t j; for (j = i; j >= 0; j--) close(fd[j]); exit(EXIT_FAILURE); } } for (k = 0; k < 1000; k++) { ret = gettimeofday(&t1, NULL); if (ret < 0) goto close_all; for (i = 0; i < sizeof(fd) / sizeof(fd[0]); i++) { ret = fstat(fd[i], &st); if (ret < 0) goto close_all; } ret = gettimeofday(&t2, NULL); if (ret < 0) goto close_all; time_in_mcs = (1000000 * t2.tv_sec + t2.tv_usec) - (1000000 * t1.tv_sec + t1.tv_usec); printf("Total time in micro seconds: %" PRIu64 "\n", time_in_mcs); printf("Total time in nanoseconds: %" PRIu64 "\n", time_in_mcs * 1000); printf("Time per file in nanoseconds: %" PRIu64 "\n", (time_in_mcs * 1000) / 1000000); times[k] = (time_in_mcs * 1000) / 1000000; } close_all: for (i = 0; i < sizeof(fd) / sizeof(fd[0]); i++) close(fd[i]); if (ret < 0) exit(EXIT_FAILURE); for (k = 0; k < 1000; k++) { sum += times[k]; } printf("Mean time per file in nanoseconds: %" PRIu64 "\n", sum / 1000); exit(EXIT_SUCCESS);; } Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com> CC: Serge Hallyn <serge@hallyn.com> CC: Eric Biederman <ebiederm@xmission.com> Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
-rw-r--r--include/linux/user_namespace.h7
-rw-r--r--kernel/user_namespace.c350
2 files changed, 324 insertions, 33 deletions
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 7c83d7f6289b..1c1046a60fb4 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -10,7 +10,8 @@
10#include <linux/sysctl.h> 10#include <linux/sysctl.h>
11#include <linux/err.h> 11#include <linux/err.h>
12 12
13#define UID_GID_MAP_MAX_EXTENTS 5 13#define UID_GID_MAP_MAX_BASE_EXTENTS 5
14#define UID_GID_MAP_MAX_EXTENTS 340
14 15
15struct uid_gid_extent { 16struct uid_gid_extent {
16 u32 first; 17 u32 first;
@@ -18,10 +19,10 @@ struct uid_gid_extent {
18 u32 count; 19 u32 count;
19}; 20};
20 21
21struct uid_gid_map { /* 64 bytes -- 1 cache line */ 22struct uid_gid_map { /* 64 bytes -- 1 cache line */
22 u32 nr_extents; 23 u32 nr_extents;
23 union { 24 union {
24 struct uid_gid_extent extent[UID_GID_MAP_MAX_EXTENTS]; 25 struct uid_gid_extent extent[UID_GID_MAP_MAX_BASE_EXTENTS];
25 struct { 26 struct {
26 struct uid_gid_extent *forward; 27 struct uid_gid_extent *forward;
27 struct uid_gid_extent *reverse; 28 struct uid_gid_extent *reverse;
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index c490f1e4313b..5fd2d53dbc75 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -23,6 +23,8 @@
23#include <linux/ctype.h> 23#include <linux/ctype.h>
24#include <linux/projid.h> 24#include <linux/projid.h>
25#include <linux/fs_struct.h> 25#include <linux/fs_struct.h>
26#include <linux/bsearch.h>
27#include <linux/sort.h>
26 28
27static struct kmem_cache *user_ns_cachep __read_mostly; 29static struct kmem_cache *user_ns_cachep __read_mostly;
28static DEFINE_MUTEX(userns_state_mutex); 30static DEFINE_MUTEX(userns_state_mutex);
@@ -181,6 +183,18 @@ static void free_user_ns(struct work_struct *work)
181 do { 183 do {
182 struct ucounts *ucounts = ns->ucounts; 184 struct ucounts *ucounts = ns->ucounts;
183 parent = ns->parent; 185 parent = ns->parent;
186 if (ns->gid_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) {
187 kfree(ns->gid_map.forward);
188 kfree(ns->gid_map.reverse);
189 }
190 if (ns->uid_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) {
191 kfree(ns->uid_map.forward);
192 kfree(ns->uid_map.reverse);
193 }
194 if (ns->projid_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) {
195 kfree(ns->projid_map.forward);
196 kfree(ns->projid_map.reverse);
197 }
184 retire_userns_sysctls(ns); 198 retire_userns_sysctls(ns);
185#ifdef CONFIG_PERSISTENT_KEYRINGS 199#ifdef CONFIG_PERSISTENT_KEYRINGS
186 key_put(ns->persistent_keyring_register); 200 key_put(ns->persistent_keyring_register);
@@ -198,7 +212,84 @@ void __put_user_ns(struct user_namespace *ns)
198} 212}
199EXPORT_SYMBOL(__put_user_ns); 213EXPORT_SYMBOL(__put_user_ns);
200 214
201static u32 map_id_range_down(struct uid_gid_map *map, u32 id, u32 count) 215/**
216 * idmap_key struct holds the information necessary to find an idmapping in a
217 * sorted idmap array. It is passed to cmp_map_id() as first argument.
218 */
219struct idmap_key {
220 bool map_up; /* true -> id from kid; false -> kid from id */
221 u32 id; /* id to find */
222 u32 count; /* == 0 unless used with map_id_range_down() */
223};
224
225/**
226 * cmp_map_id - Function to be passed to bsearch() to find the requested
227 * idmapping. Expects struct idmap_key to be passed via @k.
228 */
229static int cmp_map_id(const void *k, const void *e)
230{
231 u32 first, last, id2;
232 const struct idmap_key *key = k;
233 const struct uid_gid_extent *el = e;
234
235 /* handle map_id_range_down() */
236 if (key->count)
237 id2 = key->id + key->count - 1;
238 else
239 id2 = key->id;
240
241 /* handle map_id_{down,up}() */
242 if (key->map_up)
243 first = el->lower_first;
244 else
245 first = el->first;
246
247 last = first + el->count - 1;
248
249 if (key->id >= first && key->id <= last &&
250 (id2 >= first && id2 <= last))
251 return 0;
252
253 if (key->id < first || id2 < first)
254 return -1;
255
256 return 1;
257}
258
259/**
260 * map_id_range_down_max - Find idmap via binary search in ordered idmap array.
261 * Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS.
262 */
263static u32 map_id_range_down_max(struct uid_gid_map *map, u32 id, u32 count)
264{
265 u32 extents;
266 struct uid_gid_extent *extent;
267 struct idmap_key key;
268
269 key.map_up = false;
270 key.count = count;
271 key.id = id;
272
273 extents = map->nr_extents;
274 smp_rmb();
275
276 extent = bsearch(&key, map->forward, extents,
277 sizeof(struct uid_gid_extent), cmp_map_id);
278 /* Map the id or note failure */
279 if (extent)
280 id = (id - extent->first) + extent->lower_first;
281 else
282 id = (u32) -1;
283
284 return id;
285}
286
287/**
288 * map_id_range_down_base - Find idmap via binary search in static extent array.
289 * Can only be called if number of mappings is equal or less than
290 * UID_GID_MAP_MAX_BASE_EXTENTS.
291 */
292static u32 map_id_range_down_base(struct uid_gid_map *map, u32 id, u32 count)
202{ 293{
203 unsigned idx, extents; 294 unsigned idx, extents;
204 u32 first, last, id2; 295 u32 first, last, id2;
@@ -224,7 +315,23 @@ static u32 map_id_range_down(struct uid_gid_map *map, u32 id, u32 count)
224 return id; 315 return id;
225} 316}
226 317
227static u32 map_id_down(struct uid_gid_map *map, u32 id) 318static u32 map_id_range_down(struct uid_gid_map *map, u32 id, u32 count)
319{
320 u32 extents = map->nr_extents;
321 smp_rmb();
322
323 if (extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
324 return map_id_range_down_base(map, id, count);
325
326 return map_id_range_down_max(map, id, count);
327}
328
329/**
330 * map_id_down_base - Find idmap via binary search in static extent array.
331 * Can only be called if number of mappings is equal or less than
332 * UID_GID_MAP_MAX_BASE_EXTENTS.
333 */
334static u32 map_id_down_base(struct uid_gid_map *map, u32 id)
228{ 335{
229 unsigned idx, extents; 336 unsigned idx, extents;
230 u32 first, last; 337 u32 first, last;
@@ -247,7 +354,23 @@ static u32 map_id_down(struct uid_gid_map *map, u32 id)
247 return id; 354 return id;
248} 355}
249 356
250static u32 map_id_up(struct uid_gid_map *map, u32 id) 357static u32 map_id_down(struct uid_gid_map *map, u32 id)
358{
359 u32 extents = map->nr_extents;
360 smp_rmb();
361
362 if (extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
363 return map_id_down_base(map, id);
364
365 return map_id_range_down_max(map, id, 0);
366}
367
368/**
369 * map_id_up_base - Find idmap via binary search in static extent array.
370 * Can only be called if number of mappings is equal or less than
371 * UID_GID_MAP_MAX_BASE_EXTENTS.
372 */
373static u32 map_id_up_base(struct uid_gid_map *map, u32 id)
251{ 374{
252 unsigned idx, extents; 375 unsigned idx, extents;
253 u32 first, last; 376 u32 first, last;
@@ -271,6 +394,45 @@ static u32 map_id_up(struct uid_gid_map *map, u32 id)
271} 394}
272 395
273/** 396/**
397 * map_id_up_max - Find idmap via binary search in ordered idmap array.
398 * Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS.
399 */
400static u32 map_id_up_max(struct uid_gid_map *map, u32 id)
401{
402 u32 extents;
403 struct uid_gid_extent *extent;
404 struct idmap_key key;
405
406 key.map_up = true;
407 key.count = 0;
408 key.id = id;
409
410 extents = map->nr_extents;
411 smp_rmb();
412
413 extent = bsearch(&key, map->reverse, extents,
414 sizeof(struct uid_gid_extent), cmp_map_id);
415 /* Map the id or note failure */
416 if (extent)
417 id = (id - extent->lower_first) + extent->first;
418 else
419 id = (u32) -1;
420
421 return id;
422}
423
424static u32 map_id_up(struct uid_gid_map *map, u32 id)
425{
426 u32 extents = map->nr_extents;
427 smp_rmb();
428
429 if (extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
430 return map_id_up_base(map, id);
431
432 return map_id_up_max(map, id);
433}
434
435/**
274 * make_kuid - Map a user-namespace uid pair into a kuid. 436 * make_kuid - Map a user-namespace uid pair into a kuid.
275 * @ns: User namespace that the uid is in 437 * @ns: User namespace that the uid is in
276 * @uid: User identifier 438 * @uid: User identifier
@@ -540,13 +702,15 @@ static int projid_m_show(struct seq_file *seq, void *v)
540static void *m_start(struct seq_file *seq, loff_t *ppos, 702static void *m_start(struct seq_file *seq, loff_t *ppos,
541 struct uid_gid_map *map) 703 struct uid_gid_map *map)
542{ 704{
543 struct uid_gid_extent *extent = NULL;
544 loff_t pos = *ppos; 705 loff_t pos = *ppos;
545 706
546 if (pos < map->nr_extents) 707 if (pos >= map->nr_extents)
547 extent = &map->extent[pos]; 708 return NULL;
709
710 if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
711 return &map->extent[pos];
548 712
549 return extent; 713 return &map->forward[pos];
550} 714}
551 715
552static void *uid_m_start(struct seq_file *seq, loff_t *ppos) 716static void *uid_m_start(struct seq_file *seq, loff_t *ppos)
@@ -618,7 +782,10 @@ static bool mappings_overlap(struct uid_gid_map *new_map,
618 u32 prev_upper_last, prev_lower_last; 782 u32 prev_upper_last, prev_lower_last;
619 struct uid_gid_extent *prev; 783 struct uid_gid_extent *prev;
620 784
621 prev = &new_map->extent[idx]; 785 if (new_map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
786 prev = &new_map->extent[idx];
787 else
788 prev = &new_map->forward[idx];
622 789
623 prev_upper_first = prev->first; 790 prev_upper_first = prev->first;
624 prev_lower_first = prev->lower_first; 791 prev_lower_first = prev->lower_first;
@@ -638,6 +805,102 @@ static bool mappings_overlap(struct uid_gid_map *new_map,
638 return false; 805 return false;
639} 806}
640 807
808/**
809 * insert_extent - Safely insert a new idmap extent into struct uid_gid_map.
810 * Takes care to allocate a 4K block of memory if the number of mappings exceeds
811 * UID_GID_MAP_MAX_BASE_EXTENTS.
812 */
813static int insert_extent(struct uid_gid_map *map, struct uid_gid_extent *extent)
814{
815 if (map->nr_extents < UID_GID_MAP_MAX_BASE_EXTENTS) {
816 map->extent[map->nr_extents].first = extent->first;
817 map->extent[map->nr_extents].lower_first = extent->lower_first;
818 map->extent[map->nr_extents].count = extent->count;
819 return 0;
820 }
821
822 if (map->nr_extents == UID_GID_MAP_MAX_BASE_EXTENTS) {
823 struct uid_gid_extent *forward;
824
825 /* Allocate memory for 340 mappings. */
826 forward = kmalloc(sizeof(struct uid_gid_extent) *
827 UID_GID_MAP_MAX_EXTENTS, GFP_KERNEL);
828 if (!forward)
829 return -ENOMEM;
830
831 /* Copy over memory. Only set up memory for the forward pointer.
832 * Defer the memory setup for the reverse pointer.
833 */
834 memcpy(forward, map->extent,
835 map->nr_extents * sizeof(map->extent[0]));
836
837 map->forward = forward;
838 map->reverse = NULL;
839 }
840
841 map->forward[map->nr_extents].first = extent->first;
842 map->forward[map->nr_extents].lower_first = extent->lower_first;
843 map->forward[map->nr_extents].count = extent->count;
844 return 0;
845}
846
847/* cmp function to sort() forward mappings */
848static int cmp_extents_forward(const void *a, const void *b)
849{
850 const struct uid_gid_extent *e1 = a;
851 const struct uid_gid_extent *e2 = b;
852
853 if (e1->first < e2->first)
854 return -1;
855
856 if (e1->first > e2->first)
857 return 1;
858
859 return 0;
860}
861
862/* cmp function to sort() reverse mappings */
863static int cmp_extents_reverse(const void *a, const void *b)
864{
865 const struct uid_gid_extent *e1 = a;
866 const struct uid_gid_extent *e2 = b;
867
868 if (e1->lower_first < e2->lower_first)
869 return -1;
870
871 if (e1->lower_first > e2->lower_first)
872 return 1;
873
874 return 0;
875}
876
877/**
878 * sort_idmaps - Sorts an array of idmap entries.
879 * Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS.
880 */
881static int sort_idmaps(struct uid_gid_map *map)
882{
883 if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
884 return 0;
885
886 /* Sort forward array. */
887 sort(map->forward, map->nr_extents, sizeof(struct uid_gid_extent),
888 cmp_extents_forward, NULL);
889
890 /* Only copy the memory from forward we actually need. */
891 map->reverse = kmemdup(map->forward,
892 map->nr_extents * sizeof(struct uid_gid_extent),
893 GFP_KERNEL);
894 if (!map->reverse)
895 return -ENOMEM;
896
897 /* Sort reverse array. */
898 sort(map->reverse, map->nr_extents, sizeof(struct uid_gid_extent),
899 cmp_extents_reverse, NULL);
900
901 return 0;
902}
903
641static ssize_t map_write(struct file *file, const char __user *buf, 904static ssize_t map_write(struct file *file, const char __user *buf,
642 size_t count, loff_t *ppos, 905 size_t count, loff_t *ppos,
643 int cap_setid, 906 int cap_setid,
@@ -648,7 +911,7 @@ static ssize_t map_write(struct file *file, const char __user *buf,
648 struct user_namespace *ns = seq->private; 911 struct user_namespace *ns = seq->private;
649 struct uid_gid_map new_map; 912 struct uid_gid_map new_map;
650 unsigned idx; 913 unsigned idx;
651 struct uid_gid_extent *extent = NULL; 914 struct uid_gid_extent extent;
652 char *kbuf = NULL, *pos, *next_line; 915 char *kbuf = NULL, *pos, *next_line;
653 ssize_t ret = -EINVAL; 916 ssize_t ret = -EINVAL;
654 917
@@ -673,6 +936,8 @@ static ssize_t map_write(struct file *file, const char __user *buf,
673 */ 936 */
674 mutex_lock(&userns_state_mutex); 937 mutex_lock(&userns_state_mutex);
675 938
939 memset(&new_map, 0, sizeof(struct uid_gid_map));
940
676 ret = -EPERM; 941 ret = -EPERM;
677 /* Only allow one successful write to the map */ 942 /* Only allow one successful write to the map */
678 if (map->nr_extents != 0) 943 if (map->nr_extents != 0)
@@ -700,9 +965,7 @@ static ssize_t map_write(struct file *file, const char __user *buf,
700 /* Parse the user data */ 965 /* Parse the user data */
701 ret = -EINVAL; 966 ret = -EINVAL;
702 pos = kbuf; 967 pos = kbuf;
703 new_map.nr_extents = 0;
704 for (; pos; pos = next_line) { 968 for (; pos; pos = next_line) {
705 extent = &new_map.extent[new_map.nr_extents];
706 969
707 /* Find the end of line and ensure I don't look past it */ 970 /* Find the end of line and ensure I don't look past it */
708 next_line = strchr(pos, '\n'); 971 next_line = strchr(pos, '\n');
@@ -714,17 +977,17 @@ static ssize_t map_write(struct file *file, const char __user *buf,
714 } 977 }
715 978
716 pos = skip_spaces(pos); 979 pos = skip_spaces(pos);
717 extent->first = simple_strtoul(pos, &pos, 10); 980 extent.first = simple_strtoul(pos, &pos, 10);
718 if (!isspace(*pos)) 981 if (!isspace(*pos))
719 goto out; 982 goto out;
720 983
721 pos = skip_spaces(pos); 984 pos = skip_spaces(pos);
722 extent->lower_first = simple_strtoul(pos, &pos, 10); 985 extent.lower_first = simple_strtoul(pos, &pos, 10);
723 if (!isspace(*pos)) 986 if (!isspace(*pos))
724 goto out; 987 goto out;
725 988
726 pos = skip_spaces(pos); 989 pos = skip_spaces(pos);
727 extent->count = simple_strtoul(pos, &pos, 10); 990 extent.count = simple_strtoul(pos, &pos, 10);
728 if (*pos && !isspace(*pos)) 991 if (*pos && !isspace(*pos))
729 goto out; 992 goto out;
730 993
@@ -734,29 +997,33 @@ static ssize_t map_write(struct file *file, const char __user *buf,
734 goto out; 997 goto out;
735 998
736 /* Verify we have been given valid starting values */ 999 /* Verify we have been given valid starting values */
737 if ((extent->first == (u32) -1) || 1000 if ((extent.first == (u32) -1) ||
738 (extent->lower_first == (u32) -1)) 1001 (extent.lower_first == (u32) -1))
739 goto out; 1002 goto out;
740 1003
741 /* Verify count is not zero and does not cause the 1004 /* Verify count is not zero and does not cause the
742 * extent to wrap 1005 * extent to wrap
743 */ 1006 */
744 if ((extent->first + extent->count) <= extent->first) 1007 if ((extent.first + extent.count) <= extent.first)
745 goto out; 1008 goto out;
746 if ((extent->lower_first + extent->count) <= 1009 if ((extent.lower_first + extent.count) <=
747 extent->lower_first) 1010 extent.lower_first)
748 goto out; 1011 goto out;
749 1012
750 /* Do the ranges in extent overlap any previous extents? */ 1013 /* Do the ranges in extent overlap any previous extents? */
751 if (mappings_overlap(&new_map, extent)) 1014 if (mappings_overlap(&new_map, &extent))
752 goto out; 1015 goto out;
753 1016
754 new_map.nr_extents++; 1017 if ((new_map.nr_extents + 1) == UID_GID_MAP_MAX_EXTENTS &&
755
756 /* Fail if the file contains too many extents */
757 if ((new_map.nr_extents == UID_GID_MAP_MAX_EXTENTS) &&
758 (next_line != NULL)) 1018 (next_line != NULL))
759 goto out; 1019 goto out;
1020
1021 ret = insert_extent(&new_map, &extent);
1022 if (ret < 0)
1023 goto out;
1024 ret = -EINVAL;
1025
1026 new_map.nr_extents++;
760 } 1027 }
761 /* Be very certaint the new map actually exists */ 1028 /* Be very certaint the new map actually exists */
762 if (new_map.nr_extents == 0) 1029 if (new_map.nr_extents == 0)
@@ -767,16 +1034,26 @@ static ssize_t map_write(struct file *file, const char __user *buf,
767 if (!new_idmap_permitted(file, ns, cap_setid, &new_map)) 1034 if (!new_idmap_permitted(file, ns, cap_setid, &new_map))
768 goto out; 1035 goto out;
769 1036
1037 ret = sort_idmaps(&new_map);
1038 if (ret < 0)
1039 goto out;
1040
1041 ret = -EPERM;
770 /* Map the lower ids from the parent user namespace to the 1042 /* Map the lower ids from the parent user namespace to the
771 * kernel global id space. 1043 * kernel global id space.
772 */ 1044 */
773 for (idx = 0; idx < new_map.nr_extents; idx++) { 1045 for (idx = 0; idx < new_map.nr_extents; idx++) {
1046 struct uid_gid_extent *e;
774 u32 lower_first; 1047 u32 lower_first;
775 extent = &new_map.extent[idx]; 1048
1049 if (new_map.nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
1050 e = &new_map.extent[idx];
1051 else
1052 e = &new_map.forward[idx];
776 1053
777 lower_first = map_id_range_down(parent_map, 1054 lower_first = map_id_range_down(parent_map,
778 extent->lower_first, 1055 e->lower_first,
779 extent->count); 1056 e->count);
780 1057
781 /* Fail if we can not map the specified extent to 1058 /* Fail if we can not map the specified extent to
782 * the kernel global id space. 1059 * the kernel global id space.
@@ -784,18 +1061,31 @@ static ssize_t map_write(struct file *file, const char __user *buf,
784 if (lower_first == (u32) -1) 1061 if (lower_first == (u32) -1)
785 goto out; 1062 goto out;
786 1063
787 extent->lower_first = lower_first; 1064 e->lower_first = lower_first;
788 } 1065 }
789 1066
790 /* Install the map */ 1067 /* Install the map */
791 memcpy(map->extent, new_map.extent, 1068 if (new_map.nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) {
792 new_map.nr_extents*sizeof(new_map.extent[0])); 1069 memcpy(map->extent, new_map.extent,
1070 new_map.nr_extents * sizeof(new_map.extent[0]));
1071 } else {
1072 map->forward = new_map.forward;
1073 map->reverse = new_map.reverse;
1074 }
793 smp_wmb(); 1075 smp_wmb();
794 map->nr_extents = new_map.nr_extents; 1076 map->nr_extents = new_map.nr_extents;
795 1077
796 *ppos = count; 1078 *ppos = count;
797 ret = count; 1079 ret = count;
798out: 1080out:
1081 if (ret < 0 && new_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) {
1082 kfree(new_map.forward);
1083 kfree(new_map.reverse);
1084 map->forward = NULL;
1085 map->reverse = NULL;
1086 map->nr_extents = 0;
1087 }
1088
799 mutex_unlock(&userns_state_mutex); 1089 mutex_unlock(&userns_state_mutex);
800 kfree(kbuf); 1090 kfree(kbuf);
801 return ret; 1091 return ret;