aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/groups.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/groups.c')
-rw-r--r--kernel/groups.c67
1 files changed, 21 insertions, 46 deletions
diff --git a/kernel/groups.c b/kernel/groups.c
index 74d431d25251..2fcadd66a8fd 100644
--- a/kernel/groups.c
+++ b/kernel/groups.c
@@ -7,55 +7,31 @@
7#include <linux/security.h> 7#include <linux/security.h>
8#include <linux/syscalls.h> 8#include <linux/syscalls.h>
9#include <linux/user_namespace.h> 9#include <linux/user_namespace.h>
10#include <linux/vmalloc.h>
10#include <asm/uaccess.h> 11#include <asm/uaccess.h>
11 12
12struct group_info *groups_alloc(int gidsetsize) 13struct group_info *groups_alloc(int gidsetsize)
13{ 14{
14 struct group_info *group_info; 15 struct group_info *gi;
15 int nblocks; 16 unsigned int len;
16 int i; 17
17 18 len = sizeof(struct group_info) + sizeof(kgid_t) * gidsetsize;
18 nblocks = (gidsetsize + NGROUPS_PER_BLOCK - 1) / NGROUPS_PER_BLOCK; 19 gi = kmalloc(len, GFP_KERNEL_ACCOUNT|__GFP_NOWARN|__GFP_NORETRY);
19 /* Make sure we always allocate at least one indirect block pointer */ 20 if (!gi)
20 nblocks = nblocks ? : 1; 21 gi = __vmalloc(len, GFP_KERNEL_ACCOUNT|__GFP_HIGHMEM, PAGE_KERNEL);
21 group_info = kmalloc(sizeof(*group_info) + nblocks*sizeof(gid_t *), GFP_USER); 22 if (!gi)
22 if (!group_info)
23 return NULL; 23 return NULL;
24 group_info->ngroups = gidsetsize;
25 group_info->nblocks = nblocks;
26 atomic_set(&group_info->usage, 1);
27
28 if (gidsetsize <= NGROUPS_SMALL)
29 group_info->blocks[0] = group_info->small_block;
30 else {
31 for (i = 0; i < nblocks; i++) {
32 kgid_t *b;
33 b = (void *)__get_free_page(GFP_USER);
34 if (!b)
35 goto out_undo_partial_alloc;
36 group_info->blocks[i] = b;
37 }
38 }
39 return group_info;
40 24
41out_undo_partial_alloc: 25 atomic_set(&gi->usage, 1);
42 while (--i >= 0) { 26 gi->ngroups = gidsetsize;
43 free_page((unsigned long)group_info->blocks[i]); 27 return gi;
44 }
45 kfree(group_info);
46 return NULL;
47} 28}
48 29
49EXPORT_SYMBOL(groups_alloc); 30EXPORT_SYMBOL(groups_alloc);
50 31
51void groups_free(struct group_info *group_info) 32void groups_free(struct group_info *group_info)
52{ 33{
53 if (group_info->blocks[0] != group_info->small_block) { 34 kvfree(group_info);
54 int i;
55 for (i = 0; i < group_info->nblocks; i++)
56 free_page((unsigned long)group_info->blocks[i]);
57 }
58 kfree(group_info);
59} 35}
60 36
61EXPORT_SYMBOL(groups_free); 37EXPORT_SYMBOL(groups_free);
@@ -70,7 +46,7 @@ static int groups_to_user(gid_t __user *grouplist,
70 46
71 for (i = 0; i < count; i++) { 47 for (i = 0; i < count; i++) {
72 gid_t gid; 48 gid_t gid;
73 gid = from_kgid_munged(user_ns, GROUP_AT(group_info, i)); 49 gid = from_kgid_munged(user_ns, group_info->gid[i]);
74 if (put_user(gid, grouplist+i)) 50 if (put_user(gid, grouplist+i))
75 return -EFAULT; 51 return -EFAULT;
76 } 52 }
@@ -95,7 +71,7 @@ static int groups_from_user(struct group_info *group_info,
95 if (!gid_valid(kgid)) 71 if (!gid_valid(kgid))
96 return -EINVAL; 72 return -EINVAL;
97 73
98 GROUP_AT(group_info, i) = kgid; 74 group_info->gid[i] = kgid;
99 } 75 }
100 return 0; 76 return 0;
101} 77}
@@ -115,15 +91,14 @@ static void groups_sort(struct group_info *group_info)
115 for (base = 0; base < max; base++) { 91 for (base = 0; base < max; base++) {
116 int left = base; 92 int left = base;
117 int right = left + stride; 93 int right = left + stride;
118 kgid_t tmp = GROUP_AT(group_info, right); 94 kgid_t tmp = group_info->gid[right];
119 95
120 while (left >= 0 && gid_gt(GROUP_AT(group_info, left), tmp)) { 96 while (left >= 0 && gid_gt(group_info->gid[left], tmp)) {
121 GROUP_AT(group_info, right) = 97 group_info->gid[right] = group_info->gid[left];
122 GROUP_AT(group_info, left);
123 right = left; 98 right = left;
124 left -= stride; 99 left -= stride;
125 } 100 }
126 GROUP_AT(group_info, right) = tmp; 101 group_info->gid[right] = tmp;
127 } 102 }
128 stride /= 3; 103 stride /= 3;
129 } 104 }
@@ -141,9 +116,9 @@ int groups_search(const struct group_info *group_info, kgid_t grp)
141 right = group_info->ngroups; 116 right = group_info->ngroups;
142 while (left < right) { 117 while (left < right) {
143 unsigned int mid = (left+right)/2; 118 unsigned int mid = (left+right)/2;
144 if (gid_gt(grp, GROUP_AT(group_info, mid))) 119 if (gid_gt(grp, group_info->gid[mid]))
145 left = mid + 1; 120 left = mid + 1;
146 else if (gid_lt(grp, GROUP_AT(group_info, mid))) 121 else if (gid_lt(grp, group_info->gid[mid]))
147 right = mid; 122 right = mid;
148 else 123 else
149 return 1; 124 return 1;