diff -ruN virgin-2.5/fs/proc/array.c linux-2.5/fs/proc/array.c --- virgin-2.5/fs/proc/array.c Tue Oct 1 16:35:03 2002 +++ linux-2.5/fs/proc/array.c Fri Oct 4 13:44:38 2002 @@ -171,7 +171,7 @@ p->files ? p->files->max_fds : 0); task_unlock(p); - for (g = 0; g < p->ngroups; g++) + for (g = 0; g < min(p->ngroups, OLD_NGROUPS); g++) buffer += sprintf(buffer, "%d ", p->groups[g]); buffer += sprintf(buffer, "\n"); diff -ruN virgin-2.5/include/asm-i386/param.h linux-2.5/include/asm-i386/param.h --- virgin-2.5/include/asm-i386/param.h Tue Oct 1 16:44:14 2002 +++ linux-2.5/include/asm-i386/param.h Fri Oct 4 13:44:38 2002 @@ -13,10 +13,6 @@ #define EXEC_PAGESIZE 4096 -#ifndef NGROUPS -#define NGROUPS 32 -#endif - #ifndef NOGROUP #define NOGROUP (-1) #endif diff -ruN virgin-2.5/include/linux/init_task.h linux-2.5/include/linux/init_task.h --- virgin-2.5/include/linux/init_task.h Tue Oct 1 16:42:52 2002 +++ linux-2.5/include/linux/init_task.h Fri Oct 4 13:44:17 2002 @@ -80,6 +80,7 @@ .real_timer = { \ .function = it_real_fn \ }, \ + .ngroups = 0, \ .cap_effective = CAP_INIT_EFF_SET, \ .cap_inheritable = CAP_INIT_INH_SET, \ .cap_permitted = CAP_FULL_SET, \ diff -ruN virgin-2.5/include/linux/limits.h linux-2.5/include/linux/limits.h --- virgin-2.5/include/linux/limits.h Tue Oct 1 16:43:11 2002 +++ linux-2.5/include/linux/limits.h Fri Oct 4 13:44:38 2002 @@ -3,7 +3,6 @@ #define NR_OPEN 1024 -#define NGROUPS_MAX 32 /* supplemental group IDs are available */ #define ARG_MAX 131072 /* # bytes of args + environ for exec() */ #define CHILD_MAX 999 /* no limit :-) */ #define OPEN_MAX 256 /* # open files a process may have */ @@ -19,4 +18,6 @@ #define RTSIG_MAX 32 +#define OLD_NGROUPS 32 /* old limit of supplemental group IDs */ + #endif diff -ruN virgin-2.5/include/linux/sched.h linux-2.5/include/linux/sched.h --- virgin-2.5/include/linux/sched.h Tue Oct 1 16:42:52 2002 +++ linux-2.5/include/linux/sched.h Fri Oct 4 13:44:17 2002 @@ -351,7 +351,8 @@ uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; int ngroups; - gid_t groups[NGROUPS]; + gid_t *groups; + atomic_t *groups_refcount; kernel_cap_t cap_effective, cap_inheritable, cap_permitted; int keep_capabilities:1; struct user_struct *user; diff -ruN virgin-2.5/kernel/exit.c linux-2.5/kernel/exit.c --- virgin-2.5/kernel/exit.c Tue Oct 1 16:46:42 2002 +++ linux-2.5/kernel/exit.c Fri Oct 4 13:44:17 2002 @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,12 @@ BUG(); if (p != current) wait_task_inactive(p); + + if (p->ngroups && atomic_dec_and_test(p->groups_refcount)) { + vfree(p->groups_refcount); + vfree(p->groups); + } + atomic_dec(&p->user->processes); security_ops->task_free_security(p); free_uid(p->user); diff -ruN virgin-2.5/kernel/fork.c linux-2.5/kernel/fork.c --- virgin-2.5/kernel/fork.c Tue Oct 1 16:46:41 2002 +++ linux-2.5/kernel/fork.c Fri Oct 4 13:44:17 2002 @@ -805,6 +805,10 @@ */ clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE); + /* increment the groups ref count */ + if (p->ngroups) + atomic_inc(p->groups_refcount); + /* Our parent execution domain becomes current domain These must match for thread signalling to apply */ diff -ruN virgin-2.5/kernel/sys.c linux-2.5/kernel/sys.c --- virgin-2.5/kernel/sys.c Tue Oct 1 16:46:41 2002 +++ linux-2.5/kernel/sys.c Fri Oct 4 13:44:46 2002 @@ -20,6 +20,9 @@ #include #include #include +#include +#include + #include #include @@ -1056,42 +1059,87 @@ return i; } +static int gid_t_cmp(const void *a, const void *b) +{ + return *((gid_t *)a) - *((gid_t *)b); +} + /* - * SMP: Our groups are not shared. We can copy to/from them safely + * SMP: Our groups are copy-on-write. We can set them safely * without another task interfering. */ +int do_setgroups(int gidsetsize, gid_t *grouplist) +{ + atomic_t *newrefcnt = NULL; + + BUG_ON(gidsetsize && !grouplist); + if (gidsetsize) { + newrefcnt = vmalloc(sizeof(*newrefcnt)); + if (!newrefcnt) { + vfree(grouplist); + return -ENOMEM; + } + + atomic_set(newrefcnt, 1); + + /* sort the groupslist for faster searches */ + qsort(grouplist, gidsetsize, sizeof(gid_t), gid_t_cmp); + } + + /* disassociate ourselves from any shared group list */ + if (current->ngroups + && atomic_dec_and_test(current->groups_refcount)) { + vfree(current->groups_refcount); + vfree(current->groups); + } + + current->groups = grouplist; + current->groups_refcount = newrefcnt; + current->ngroups = gidsetsize; + + return 0; +} asmlinkage long sys_setgroups(int gidsetsize, gid_t *grouplist) { - gid_t groups[NGROUPS]; + gid_t *groups = NULL; int retval; if (!capable(CAP_SETGID)) return -EPERM; - if ((unsigned) gidsetsize > NGROUPS) - return -EINVAL; - if(copy_from_user(groups, grouplist, gidsetsize * sizeof(gid_t))) - return -EFAULT; + if (gidsetsize) { + /* + * make sure there is at least OLD_NGROUPS amount of space in + * the group list for backwards compatiblity sake. + */ + int alloc_size = (gidsetsize > OLD_NGROUPS) ? + gidsetsize : OLD_NGROUPS; + groups = vmalloc(alloc_size * sizeof(gid_t)); + if (!groups) + return -ENOMEM; + + if (copy_from_user(groups, grouplist, + gidsetsize * sizeof(gid_t))) { + vfree(groups); + return -EFAULT; + } + } + retval = security_ops->task_setgroups(gidsetsize, groups); - if (retval) + if (retval) { + if (groups) + vfree(groups); return retval; - memcpy(current->groups, groups, gidsetsize * sizeof(gid_t)); - current->ngroups = gidsetsize; - return 0; + } + return do_setgroups(gidsetsize, groups); } static int supplemental_group_member(gid_t grp) { - int i = current->ngroups; - - if (i) { - gid_t *groups = current->groups; - do { - if (*groups == grp) - return 1; - groups++; - i--; - } while (i); + if (current->ngroups) { + if (bsearch(&grp, current->groups, current->ngroups, + sizeof(gid_t), gid_t_cmp)) + return 1; } return 0; } @@ -1384,3 +1432,4 @@ EXPORT_SYMBOL(unregister_reboot_notifier); EXPORT_SYMBOL(in_group_p); EXPORT_SYMBOL(in_egroup_p); +EXPORT_SYMBOL(sys_setgroups); diff -ruN virgin-2.5/kernel/uid16.c linux-2.5/kernel/uid16.c --- virgin-2.5/kernel/uid16.c Tue Oct 1 16:46:42 2002 +++ linux-2.5/kernel/uid16.c Fri Oct 4 13:48:39 2002 @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -27,6 +28,7 @@ extern asmlinkage long sys_setresgid(gid_t, gid_t, gid_t); extern asmlinkage long sys_setfsuid(uid_t); extern asmlinkage long sys_setfsgid(gid_t); +extern int do_setgroups(int gidsetsize, gid_t *grouplist); asmlinkage long sys_chown16(const char * filename, old_uid_t user, old_gid_t group) { @@ -109,43 +111,74 @@ asmlinkage long sys_getgroups16(int gidsetsize, old_gid_t *grouplist) { - old_gid_t groups[NGROUPS]; + old_gid_t *groups; int i,j; if (gidsetsize < 0) return -EINVAL; i = current->ngroups; - if (gidsetsize) { + if (i && gidsetsize) { if (i > gidsetsize) return -EINVAL; + groups = vmalloc(sizeof(old_gid_t) * i); + if (!groups) + return -ENOMEM; for(j=0;jgroups[j]; - if (copy_to_user(grouplist, groups, sizeof(old_gid_t)*i)) + if (copy_to_user(grouplist, groups, sizeof(old_gid_t)*i)) { + vfree(groups); return -EFAULT; + } + vfree(groups); } return i; } asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t *grouplist) { - old_gid_t groups[NGROUPS]; - gid_t new_groups[NGROUPS]; + old_gid_t *groups; + gid_t *new_groups = NULL; int i; if (!capable(CAP_SETGID)) return -EPERM; - if ((unsigned) gidsetsize > NGROUPS) - return -EINVAL; - if (copy_from_user(groups, grouplist, gidsetsize * sizeof(old_gid_t))) - return -EFAULT; - for (i = 0 ; i < gidsetsize ; i++) - new_groups[i] = (gid_t)groups[i]; + if (gidsetsize) { + /* + * make sure there is at least OLD_NGROUPS amount of space in + * the group list for backwards compatiblity sake. + */ + int alloc_size = (gidsetsize > OLD_NGROUPS) ? + gidsetsize : OLD_NGROUPS; + + groups = vmalloc(sizeof(old_gid_t) * gidsetsize); + if (!groups) + return -ENOMEM; + + if (copy_from_user(groups, grouplist, + gidsetsize * sizeof(old_gid_t))) { + vfree(groups); + return -EFAULT; + } + + if (!(new_groups = vmalloc(sizeof(gid_t) * alloc_size))) { + vfree(groups); + return -ENOMEM; + } + + for (i = 0; i < gidsetsize; i++) + new_groups[i] = (gid_t)groups[i]; + + vfree(groups); + } + i = security_ops->task_setgroups(gidsetsize, new_groups); - if (i) + if (i) { + if (new_groups) + vfree(new_groups); return i; - memcpy(current->groups, new_groups, gidsetsize * sizeof(gid_t)); - current->ngroups = gidsetsize; - return 0; + } + /* handles the vmalloc()ed new_groups */ + return do_setgroups(gidsetsize, new_groups); } asmlinkage long sys_getuid16(void)