2003-09-29 07:21:52

by Rusty Russell

[permalink] [raw]
Subject: [PATCH] Many groups patch.

As stated before, SAMBA customers want > 200 groups. Special thanks
to Tim Hockin for feedback based on his patch.

This version drops the internal groups array (it's so often shared
that it's not worth it, and the logic becomes a bit neater), and does
vmalloc fallback in case someone has massive number of groups.

Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.

Name: Dynamic Allocation of Groups Array When Required: With Refcounting
Author: Rusty Russell
Status: Tested on 2.6.0-test6
Depends: Misc/qemu-page-offset.patch.gz

D: This patch allows the maximum number of groups to be varied using
D: sysctl. Since sharing is so common, we use a refcounted external
D: array for groups.
D:
D: Changes:
D: 1) Remove the NGROUPS define from archs.
D: 2) Fixup the few places which declare [NGROUPS] arrays on the stack.
D: 3) The ia64, s390 and sparc64 ports have their own setgroups/getgroups
D: implementations: unify them on the ia64 one, which calls the core
D: functions.
D: 4) Change the task_struct's groups to a pointer to inside a refcounted
D: external array, fix up fork() to inc refcount.
D: 5) Introduce max_groups and use it instead of NGROUPS.
D: 6) Add a sysctl to vary max_groups.
D:
D: This patch scars nfs: artificially restrict the groups there to
D: SVC_CRED_NGROUPS (32), which is probably wrong, but won't break if
D: they don't change the default.
D:
D: This patch breaks intermezzo: I'm not sure how they want to deal
D: with it.

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/arch/ia64/ia32/sys_ia32.c .12070-2.6.0-test6-more_groups_refcount/arch/ia64/ia32/sys_ia32.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/arch/ia64/ia32/sys_ia32.c 2003-09-29 10:25:17.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/arch/ia64/ia32/sys_ia32.c 2003-09-29 17:18:46.000000000 +1000
@@ -2432,17 +2432,24 @@ asmlinkage long
sys32_getgroups16 (int gidsetsize, short *grouplist)
{
mm_segment_t old_fs = get_fs();
- gid_t gl[NGROUPS];
+ gid_t *gl;
int ret, i;

+ gl = kmalloc(sizeof(gl[0]) * current->ngroups, GFP_KERNEL);
+ if (!gl)
+ return -ENOMEM;
+
set_fs(KERNEL_DS);
ret = sys_getgroups(gidsetsize, gl);
set_fs(old_fs);

- if (gidsetsize && ret > 0 && ret <= NGROUPS)
+ if (gidsetsize && ret > 0)
for (i = 0; i < ret; i++, grouplist++)
- if (put_user(gl[i], grouplist))
- return -EFAULT;
+ if (put_user(gl[i], grouplist)) {
+ ret = -EFAULT;
+ break;
+ }
+ kfree(gl);
return ret;
}

@@ -2452,17 +2459,23 @@ asmlinkage long
sys32_setgroups16 (int gidsetsize, short *grouplist)
{
mm_segment_t old_fs = get_fs();
- gid_t gl[NGROUPS];
+ gid_t *gl;
int ret, i;

- if ((unsigned) gidsetsize > NGROUPS)
+ if ((unsigned) gidsetsize > max_groups)
return -EINVAL;
+ gl = kmalloc(sizeof(gl[0]) * gidsetsize, GFP_KERNEL);
+ if (!gl)
+ return -ENOMEM;
for (i = 0; i < gidsetsize; i++, grouplist++)
- if (get_user(gl[i], grouplist))
+ if (get_user(gl[i], grouplist)) {
+ kfree(gl);
return -EFAULT;
+ }
set_fs(KERNEL_DS);
ret = sys_setgroups(gidsetsize, gl);
set_fs(old_fs);
+ kfree(gl);
return ret;
}

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/arch/mips/kernel/sysirix.c .12070-2.6.0-test6-more_groups_refcount/arch/mips/kernel/sysirix.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/arch/mips/kernel/sysirix.c 2003-09-29 10:25:18.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/arch/mips/kernel/sysirix.c 2003-09-29 17:18:46.000000000 +1000
@@ -368,7 +368,7 @@ asmlinkage int irix_syssgi(struct pt_reg
retval = HZ;
goto out;
case 4:
- retval = NGROUPS;
+ retval = max_groups;
goto out;
case 5:
retval = NR_OPEN;
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/arch/s390/kernel/compat_linux.c .12070-2.6.0-test6-more_groups_refcount/arch/s390/kernel/compat_linux.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/arch/s390/kernel/compat_linux.c 2003-09-29 10:25:21.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/arch/s390/kernel/compat_linux.c 2003-09-29 17:18:46.000000000 +1000
@@ -189,40 +189,57 @@ asmlinkage long sys32_setfsgid16(u16 gid
return sys_setfsgid((gid_t)gid);
}

-asmlinkage long sys32_getgroups16(int gidsetsize, u16 *grouplist)
+extern asmlinkage long sys_getgroups (int gidsetsize, gid_t *grouplist);
+
+asmlinkage long
+sys32_getgroups16 (int gidsetsize, short *grouplist)
{
- u16 groups[NGROUPS];
- int i,j;
+ mm_segment_t old_fs = get_fs();
+ gid_t *gl;
+ int ret, i;

- if (gidsetsize < 0)
- return -EINVAL;
- i = current->ngroups;
- if (gidsetsize) {
- if (i > gidsetsize)
- return -EINVAL;
- for(j=0;j<i;j++)
- groups[j] = current->groups[j];
- if (copy_to_user(grouplist, groups, sizeof(u16)*i))
- return -EFAULT;
- }
- return i;
+ gl = kmalloc(sizeof(gl[0]) * current->ngroups, GFP_KERNEL);
+ if (!gl)
+ return -ENOMEM;
+
+ set_fs(KERNEL_DS);
+ ret = sys_getgroups(gidsetsize, gl);
+ set_fs(old_fs);
+
+ if (gidsetsize && ret > 0)
+ for (i = 0; i < ret; i++, grouplist++)
+ if (put_user(gl[i], grouplist)) {
+ ret = -EFAULT;
+ break;
+ }
+ kfree(gl);
+ return ret;
}

-asmlinkage long sys32_setgroups16(int gidsetsize, u16 *grouplist)
+extern asmlinkage long sys_setgroups (int gidsetsize, gid_t *grouplist);
+
+asmlinkage long
+sys32_setgroups16 (int gidsetsize, short *grouplist)
{
- u16 groups[NGROUPS];
- int i;
+ mm_segment_t old_fs = get_fs();
+ gid_t *gl;
+ int ret, i;

- if (!capable(CAP_SETGID))
- return -EPERM;
- if ((unsigned) gidsetsize > NGROUPS)
+ if ((unsigned) gidsetsize > max_groups)
return -EINVAL;
- if (copy_from_user(groups, grouplist, gidsetsize * sizeof(u16)))
- return -EFAULT;
- for (i = 0 ; i < gidsetsize ; i++)
- current->groups[i] = (gid_t)groups[i];
- current->ngroups = gidsetsize;
- return 0;
+ gl = kmalloc(sizeof(gl[0]) * gidsetsize, GFP_KERNEL);
+ if (!gl)
+ return -ENOMEM;
+ for (i = 0; i < gidsetsize; i++, grouplist++)
+ if (get_user(gl[i], grouplist)) {
+ kfree(gl);
+ return -EFAULT;
+ }
+ set_fs(KERNEL_DS);
+ ret = sys_setgroups(gidsetsize, gl);
+ set_fs(old_fs);
+ kfree(gl);
+ return ret;
}

asmlinkage long sys32_getuid16(void)
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/arch/sparc/kernel/sys_sunos.c .12070-2.6.0-test6-more_groups_refcount/arch/sparc/kernel/sys_sunos.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/arch/sparc/kernel/sys_sunos.c 2003-09-22 10:27:56.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/arch/sparc/kernel/sys_sunos.c 2003-09-29 17:18:46.000000000 +1000
@@ -896,7 +896,7 @@ extern asmlinkage long sunos_sysconf (in
ret = HZ;
break;
case _SC_NGROUPS_MAX:
- ret = NGROUPS_MAX;
+ ret = max_groups;
break;
case _SC_OPEN_MAX:
ret = OPEN_MAX;
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/arch/sparc64/kernel/sys_sparc32.c .12070-2.6.0-test6-more_groups_refcount/arch/sparc64/kernel/sys_sparc32.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/arch/sparc64/kernel/sys_sparc32.c 2003-09-29 10:25:22.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/arch/sparc64/kernel/sys_sparc32.c 2003-09-29 17:18:46.000000000 +1000
@@ -206,40 +206,57 @@ asmlinkage long sys32_setfsgid16(u16 gid
return sys_setfsgid((gid_t)gid);
}

-asmlinkage long sys32_getgroups16(int gidsetsize, u16 *grouplist)
+extern asmlinkage long sys_getgroups (int gidsetsize, gid_t *grouplist);
+
+asmlinkage long
+sys32_getgroups16 (int gidsetsize, short *grouplist)
{
- u16 groups[NGROUPS];
- int i,j;
+ mm_segment_t old_fs = get_fs();
+ gid_t *gl;
+ int ret, i;

- if (gidsetsize < 0)
- return -EINVAL;
- i = current->ngroups;
- if (gidsetsize) {
- if (i > gidsetsize)
- return -EINVAL;
- for(j=0;j<i;j++)
- groups[j] = current->groups[j];
- if (copy_to_user(grouplist, groups, sizeof(u16)*i))
- return -EFAULT;
- }
- return i;
+ gl = kmalloc(sizeof(gl[0]) * current->ngroups, GFP_KERNEL);
+ if (!gl)
+ return -ENOMEM;
+
+ set_fs(KERNEL_DS);
+ ret = sys_getgroups(gidsetsize, gl);
+ set_fs(old_fs);
+
+ if (gidsetsize && ret > 0)
+ for (i = 0; i < ret; i++, grouplist++)
+ if (put_user(gl[i], grouplist)) {
+ ret = -EFAULT;
+ break;
+ }
+ kfree(gl);
+ return ret;
}

-asmlinkage long sys32_setgroups16(int gidsetsize, u16 *grouplist)
+extern asmlinkage long sys_setgroups (int gidsetsize, gid_t *grouplist);
+
+asmlinkage long
+sys32_setgroups16 (int gidsetsize, short *grouplist)
{
- u16 groups[NGROUPS];
- int i;
+ mm_segment_t old_fs = get_fs();
+ gid_t *gl;
+ int ret, i;

- if (!capable(CAP_SETGID))
- return -EPERM;
- if ((unsigned) gidsetsize > NGROUPS)
+ if ((unsigned) gidsetsize > max_groups)
return -EINVAL;
- if (copy_from_user(groups, grouplist, gidsetsize * sizeof(u16)))
- return -EFAULT;
- for (i = 0 ; i < gidsetsize ; i++)
- current->groups[i] = (gid_t)groups[i];
- current->ngroups = gidsetsize;
- return 0;
+ gl = kmalloc(sizeof(gl[0]) * gidsetsize, GFP_KERNEL);
+ if (!gl)
+ return -ENOMEM;
+ for (i = 0; i < gidsetsize; i++, grouplist++)
+ if (get_user(gl[i], grouplist)) {
+ kfree(gl);
+ return -EFAULT;
+ }
+ set_fs(KERNEL_DS);
+ ret = sys_setgroups(gidsetsize, gl);
+ set_fs(old_fs);
+ kfree(gl);
+ return ret;
}

asmlinkage long sys32_getuid16(void)
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/arch/sparc64/kernel/sys_sunos32.c .12070-2.6.0-test6-more_groups_refcount/arch/sparc64/kernel/sys_sunos32.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/arch/sparc64/kernel/sys_sunos32.c 2003-09-22 10:27:56.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/arch/sparc64/kernel/sys_sunos32.c 2003-09-29 17:18:46.000000000 +1000
@@ -859,7 +859,7 @@ extern asmlinkage s32 sunos_sysconf (int
ret = HZ;
break;
case _SC_NGROUPS_MAX:
- ret = NGROUPS_MAX;
+ ret = max_groups;
break;
case _SC_OPEN_MAX:
ret = OPEN_MAX;
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/fs/nfsd/auth.c .12070-2.6.0-test6-more_groups_refcount/fs/nfsd/auth.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/fs/nfsd/auth.c 2003-09-22 10:21:34.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/fs/nfsd/auth.c 2003-09-29 17:18:46.000000000 +1000
@@ -26,7 +26,7 @@ nfsd_setuser(struct svc_rqst *rqstp, str
cred->cr_uid = exp->ex_anon_uid;
if (!cred->cr_gid)
cred->cr_gid = exp->ex_anon_gid;
- for (i = 0; i < NGROUPS; i++)
+ for (i = 0; i < SVC_CRED_NGROUPS; i++)
if (!cred->cr_groups[i])
cred->cr_groups[i] = exp->ex_anon_gid;
}
@@ -39,7 +39,9 @@ nfsd_setuser(struct svc_rqst *rqstp, str
current->fsgid = cred->cr_gid;
else
current->fsgid = exp->ex_anon_gid;
- for (i = 0; i < NGROUPS; i++) {
+
+ /* We can do this because cow_current_groups() was done at birth. */
+ for (i = 0; i < SVC_CRED_NGROUPS; i++) {
gid_t group = cred->cr_groups[i];
if (group == (gid_t) NOGROUP)
break;
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/fs/nfsd/nfs4state.c .12070-2.6.0-test6-more_groups_refcount/fs/nfsd/nfs4state.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/fs/nfsd/nfs4state.c 2003-09-29 10:25:52.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/fs/nfsd/nfs4state.c 2003-09-29 17:18:46.000000000 +1000
@@ -244,7 +244,7 @@ copy_cred(struct svc_cred *target, struc

target->cr_uid = source->cr_uid;
target->cr_gid = source->cr_gid;
- for(i = 0; i < NGROUPS; i++)
+ for(i = 0; i < SVC_CREDS_NGROUPS; i++)
target->cr_groups[i] = source->cr_groups[i];
}

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/fs/nfsd/nfssvc.c .12070-2.6.0-test6-more_groups_refcount/fs/nfsd/nfssvc.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/fs/nfsd/nfssvc.c 2003-09-22 10:26:12.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/fs/nfsd/nfssvc.c 2003-09-29 17:18:46.000000000 +1000
@@ -182,6 +182,11 @@ nfsd(struct svc_rqst *rqstp)
daemonize("nfsd");
current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;

+ if (cow_current_groups(SVC_CRED_NGROUPS) < 0) {
+ printk("Unable to start nfsd thread: can't set groups\n");
+ goto out;
+ }
+
/* After daemonize() this kernel thread shares current->fs
* with the init process. We need to create files with a
* umask of 0 instead of init's umask. */
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-alpha/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-alpha/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-alpha/param.h 2003-09-21 17:27:17.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-alpha/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -19,10 +19,6 @@

#define EXEC_PAGESIZE 8192

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-arm/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-arm/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-arm/param.h 2003-09-22 10:28:10.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-arm/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -26,10 +26,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-arm26/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-arm26/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-arm26/param.h 2003-09-22 10:09:07.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-arm26/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -22,10 +22,6 @@
# define HZ 100
#endif

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-cris/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-cris/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-cris/param.h 2003-09-22 10:23:13.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-cris/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -14,10 +14,6 @@

#define EXEC_PAGESIZE 8192

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-h8300/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-h8300/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-h8300/param.h 2003-09-22 10:07:04.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-h8300/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -14,10 +14,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-i386/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-i386/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-i386/param.h 2003-09-29 17:18:46.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-i386/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -18,10 +18,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-ia64/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-ia64/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-ia64/param.h 2003-09-29 10:25:58.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-ia64/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -10,10 +10,6 @@

#define EXEC_PAGESIZE 65536

-#ifndef NGROUPS
-# define NGROUPS 32
-#endif
-
#ifndef NOGROUP
# define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-m68k/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-m68k/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-m68k/param.h 2003-09-21 17:26:43.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-m68k/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -13,10 +13,6 @@

#define EXEC_PAGESIZE 8192

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-m68knommu/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-m68knommu/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-m68knommu/param.h 2003-09-21 17:31:31.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-m68knommu/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -44,10 +44,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-mips/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-mips/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-mips/param.h 2003-09-22 10:22:44.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-mips/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -33,10 +33,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-parisc/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-parisc/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-parisc/param.h 2003-09-21 17:31:10.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-parisc/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -17,10 +17,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-ppc/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-ppc/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-ppc/param.h 2003-09-22 09:47:27.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-ppc/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -13,10 +13,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-ppc64/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-ppc64/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-ppc64/param.h 2003-09-21 17:26:44.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-ppc64/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -20,10 +20,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-s390/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-s390/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-s390/param.h 2003-09-21 17:29:29.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-s390/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -21,10 +21,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-sh/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-sh/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-sh/param.h 2003-09-22 10:23:00.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-sh/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -17,10 +17,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-sparc/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-sparc/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-sparc/param.h 2003-09-21 17:26:18.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-sparc/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -14,10 +14,6 @@

#define EXEC_PAGESIZE 8192 /* Thanks for sun4's we carry baggage... */

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-sparc64/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-sparc64/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-sparc64/param.h 2003-09-21 17:26:18.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-sparc64/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -14,10 +14,6 @@

#define EXEC_PAGESIZE 8192 /* Thanks for sun4's we carry baggage... */

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-um/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-um/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-um/param.h 2003-09-21 17:28:16.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-um/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -3,10 +3,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-v850/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-v850/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-v850/param.h 2003-09-21 17:31:32.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-v850/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -18,10 +18,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-x86_64/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-x86_64/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-x86_64/param.h 2003-09-21 17:30:30.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-x86_64/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -13,10 +13,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/init_task.h .12070-2.6.0-test6-more_groups_refcount/include/linux/init_task.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/init_task.h 2003-09-22 10:27:37.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/linux/init_task.h 2003-09-29 17:18:47.000000000 +1000
@@ -56,6 +56,8 @@
.siglock = SPIN_LOCK_UNLOCKED, \
}

+extern struct task_groups init_groups;
+
/*
* INIT_TASK is used to set up the first task table, touch at
* your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -108,6 +110,7 @@
.proc_lock = SPIN_LOCK_UNLOCKED, \
.switch_lock = SPIN_LOCK_UNLOCKED, \
.journal_info = NULL, \
+ .groups = init_groups.groups, \
}


diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/sched.h .12070-2.6.0-test6-more_groups_refcount/include/linux/sched.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/sched.h 2003-09-29 10:26:05.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/linux/sched.h 2003-09-29 17:18:47.000000000 +1000
@@ -328,6 +328,16 @@ struct k_itimer {
struct io_context; /* See blkdev.h */
void exit_io_context(void);

+/* Size is determined by task_struct's ngroups. */
+struct task_groups
+{
+ atomic_t usage;
+ void (*free)(const void *);
+ gid_t groups[0];
+};
+
+extern int max_groups;
+
struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
struct thread_info *thread_info;
@@ -403,7 +413,7 @@ struct task_struct {
uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;
int ngroups;
- gid_t groups[NGROUPS];
+ gid_t *groups; /* task_groups->groups */
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
int keep_capabilities:1;
struct user_struct *user;
@@ -602,6 +612,12 @@ extern int send_group_sigqueue(int, stru
extern int do_sigaction(int, const struct k_sigaction *, struct k_sigaction *);
extern int do_sigaltstack(const stack_t __user *, stack_t __user *, unsigned long);

+/* container_of doesn't like arrays. */
+static inline struct task_groups *task_groups(gid_t *groups)
+{
+ return (void *)groups - offsetof(struct task_groups, groups);
+}
+
/* These can be the second arg to send_sig_info/send_group_sig_info. */
#define SEND_SIG_NOINFO ((struct siginfo *) 0)
#define SEND_SIG_PRIV ((struct siginfo *) 1)
@@ -678,6 +694,10 @@ extern int do_execve(char *, char __user
extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *);
extern struct task_struct * copy_process(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *);

+/* Copy-on-write current groups to this size, if possible. 0 or -err. */
+extern int cow_current_groups(unsigned int ngroups);
+extern void release_groups(gid_t *groups);
+
#ifdef CONFIG_SMP
extern void wait_task_inactive(task_t * p);
#else
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/sunrpc/svcauth.h .12070-2.6.0-test6-more_groups_refcount/include/linux/sunrpc/svcauth.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/sunrpc/svcauth.h 2003-09-22 09:47:41.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/linux/sunrpc/svcauth.h 2003-09-29 17:18:47.000000000 +1000
@@ -16,10 +16,12 @@
#include <linux/sunrpc/cache.h>
#include <linux/hash.h>

+#define SVC_CRED_NGROUPS 32
+
struct svc_cred {
uid_t cr_uid;
gid_t cr_gid;
- gid_t cr_groups[NGROUPS];
+ gid_t cr_groups[SVC_CRED_NGROUPS];
};

struct svc_rqst; /* forward decl */
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/sysctl.h .12070-2.6.0-test6-more_groups_refcount/include/linux/sysctl.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/sysctl.h 2003-09-22 10:28:13.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/linux/sysctl.h 2003-09-29 17:18:47.000000000 +1000
@@ -127,6 +127,7 @@ enum
KERN_PANIC_ON_OOPS=57, /* int: whether we will panic on an oops */
KERN_HPPA_PWRSW=58, /* int: hppa soft-power enable */
KERN_HPPA_UNALIGNED=59, /* int: hppa unaligned-trap enable */
+ KERN_MAX_GROUPS=60, /* int: setgroups limit */
};


diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/init/main.c .12070-2.6.0-test6-more_groups_refcount/init/main.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/init/main.c 2003-09-29 10:26:06.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/init/main.c 2003-09-29 17:18:47.000000000 +1000
@@ -594,7 +594,6 @@ static int init(void * unused)
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
-
if (execute_command)
run_init_process(execute_command);

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/kernel/fork.c .12070-2.6.0-test6-more_groups_refcount/kernel/fork.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/kernel/fork.c 2003-09-29 10:26:06.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/kernel/fork.c 2003-09-29 17:18:47.000000000 +1000
@@ -85,6 +85,7 @@ void __put_task_struct(struct task_struc

security_task_free(tsk);
free_uid(tsk->user);
+ release_groups(tsk->groups);
free_task(tsk);
}

@@ -817,6 +818,7 @@ struct task_struct *copy_process(unsigne

atomic_inc(&p->user->__count);
atomic_inc(&p->user->processes);
+ atomic_inc(&task_groups(p->groups)->usage);

/*
* If multiple threads are within copy_process(), then this check
@@ -1063,6 +1065,7 @@ bad_fork_cleanup_put_domain:
bad_fork_cleanup_count:
atomic_dec(&p->user->processes);
free_uid(p->user);
+ release_groups(p->groups);
bad_fork_free:
free_task(p);
goto fork_out;
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/kernel/sys.c .12070-2.6.0-test6-more_groups_refcount/kernel/sys.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/kernel/sys.c 2003-09-29 10:26:06.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/kernel/sys.c 2003-09-29 17:18:47.000000000 +1000
@@ -1069,6 +1069,62 @@ out:
return err;
}

+/* Never freed */
+struct task_groups init_groups = { .usage = ATOMIC_INIT(2) };
+
+int max_groups = 32;
+
+void release_groups(gid_t *groups)
+{
+ struct task_groups *tgrp = task_groups(groups);
+
+ if (atomic_dec_and_test(&tgrp->usage))
+ tgrp->free(tgrp);
+}
+
+/* This does the actual copy and changeover. Caller sets
+ * current->ngroups and changes group array if everything else goes ok. */
+static inline int alloc_new_groups(unsigned int ngroups)
+{
+ struct task_groups *tgrp;
+ unsigned int size;
+
+ if (ngroups < current->ngroups)
+ ngroups = current->ngroups;
+ size = sizeof(*tgrp) + sizeof(tgrp->groups[0])*ngroups;
+
+ if (!(tgrp = kmalloc(size, GFP_KERNEL))) {
+ if (!(tgrp = vmalloc(size)))
+ return -ENOMEM;
+ tgrp->free = (void (*)(const void *))vfree;
+ } else
+ tgrp->free = kfree;
+ atomic_set(&tgrp->usage, 1);
+
+ memcpy(tgrp->groups, current->groups,
+ sizeof(tgrp->groups[0]) * current->ngroups);
+
+ release_groups(current->groups);
+ current->groups = tgrp->groups;
+ return 0;
+}
+
+/* Unshare and maybe enlarge current groups to this size, if possible.
+ * 0 or -err. */
+int cow_current_groups(unsigned int ngroups)
+{
+ if (ngroups > max_groups)
+ return -EINVAL;
+
+ printk("cow to %u groups\n", ngroups);
+ /* Shared, or needs expansion? */
+ if (atomic_read(&task_groups(current->groups)->usage) > 1
+ || ngroups > current->ngroups)
+ return alloc_new_groups(ngroups);
+
+ return 0;
+}
+
/*
* Supplementary group IDs
*/
@@ -1094,27 +1150,36 @@ asmlinkage long sys_getgroups(int gidset
}

/*
- * SMP: Our groups are not shared. We can copy to/from them safely
+ * SMP: Our groups are copy-on-write. We can copy to/from them safely
* without another task interfering.
*/

asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist)
{
- gid_t groups[NGROUPS];
+ gid_t *groups;
int retval;

+ printk("sys_setgroups %u groups\n", gidsetsize);
if (!capable(CAP_SETGID))
return -EPERM;
- if ((unsigned) gidsetsize > NGROUPS)
- return -EINVAL;
- if (copy_from_user(groups, grouplist, gidsetsize * sizeof(gid_t)))
- return -EFAULT;
+ retval = cow_current_groups(gidsetsize);
+ if (retval < 0)
+ return retval;
+ groups = kmalloc(sizeof(groups[0]) * gidsetsize, GFP_KERNEL);
+ if (!groups)
+ return -ENOMEM;
+ if (copy_from_user(groups, grouplist, gidsetsize * sizeof(gid_t))) {
+ retval = -EFAULT;
+ goto out;
+ }
retval = security_task_setgroups(gidsetsize, groups);
if (retval)
- return retval;
+ goto out;
memcpy(current->groups, groups, gidsetsize * sizeof(gid_t));
current->ngroups = gidsetsize;
- return 0;
+out:
+ kfree(groups);
+ return retval;
}

static int supplemental_group_member(gid_t grp)
@@ -1434,6 +1499,7 @@ asmlinkage long sys_prctl(int option, un
return error;
}

+EXPORT_SYMBOL(cow_current_groups);
EXPORT_SYMBOL(notifier_chain_register);
EXPORT_SYMBOL(notifier_chain_unregister);
EXPORT_SYMBOL(notifier_call_chain);
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/kernel/sysctl.c .12070-2.6.0-test6-more_groups_refcount/kernel/sysctl.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/kernel/sysctl.c 2003-09-29 10:26:06.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/kernel/sysctl.c 2003-09-29 17:18:47.000000000 +1000
@@ -581,6 +581,14 @@ static ctl_table kern_table[] = {
.mode = 0644,
.proc_handler = &proc_dointvec,
},
+ {
+ .ctl_name = KERN_MAX_GROUPS,
+ .procname = "max_groups",
+ .data = &max_groups,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
{ .ctl_name = 0 }
};

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/kernel/uid16.c .12070-2.6.0-test6-more_groups_refcount/kernel/uid16.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/kernel/uid16.c 2003-09-22 10:07:19.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/kernel/uid16.c 2003-09-29 17:18:47.000000000 +1000
@@ -109,7 +109,6 @@ asmlinkage long sys_setfsgid16(old_gid_t

asmlinkage long sys_getgroups16(int gidsetsize, old_gid_t __user *grouplist)
{
- old_gid_t groups[NGROUPS];
int i,j;

if (gidsetsize < 0)
@@ -118,34 +117,48 @@ asmlinkage long sys_getgroups16(int gids
if (gidsetsize) {
if (i > gidsetsize)
return -EINVAL;
- for(j=0;j<i;j++)
- groups[j] = current->groups[j];
- if (copy_to_user(grouplist, groups, sizeof(old_gid_t)*i))
+ if (!access_ok(VERIFY_WRITE, grouplist, sizeof(old_gid_t)*i))
return -EFAULT;
+ for(j=0;j<i;j++) {
+ old_gid_t group;
+ group = current->groups[j];
+ if (copy_to_user(grouplist+j, &group, sizeof(group)))
+ return -EFAULT;
+ }
}
return i;
}

asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t __user *grouplist)
{
- old_gid_t groups[NGROUPS];
- gid_t new_groups[NGROUPS];
- int i;
+ old_gid_t *groups;
+ gid_t *new_groups;
+ int i, ret;

if (!capable(CAP_SETGID))
return -EPERM;
- if ((unsigned) gidsetsize > NGROUPS)
- return -EINVAL;
+
+ if ((ret = cow_current_groups(gidsetsize)) < 0)
+ return ret;
+ ret = -ENOMEM;
+ groups = kmalloc(sizeof(groups[0]) * gidsetsize, GFP_KERNEL);
+ new_groups = kmalloc(sizeof(new_groups[0]) * gidsetsize, GFP_KERNEL);
+ if (!groups || !new_groups)
+ goto out;
+ ret = -EFAULT;
if (copy_from_user(groups, grouplist, gidsetsize * sizeof(old_gid_t)))
- return -EFAULT;
+ goto out;
for (i = 0 ; i < gidsetsize ; i++)
new_groups[i] = (gid_t)groups[i];
- i = security_task_setgroups(gidsetsize, new_groups);
- if (i)
- return i;
+ ret = security_task_setgroups(gidsetsize, new_groups);
+ if (ret)
+ goto out;
memcpy(current->groups, new_groups, gidsetsize * sizeof(gid_t));
current->ngroups = gidsetsize;
- return 0;
+out:
+ kfree(groups);
+ kfree(groups);
+ return ret;
}

asmlinkage long sys_getuid16(void)
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/net/sunrpc/svcauth_unix.c .12070-2.6.0-test6-more_groups_refcount/net/sunrpc/svcauth_unix.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/net/sunrpc/svcauth_unix.c 2003-09-22 10:23:05.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/net/sunrpc/svcauth_unix.c 2003-09-29 17:18:47.000000000 +1000
@@ -434,11 +434,11 @@ svcauth_unix_accept(struct svc_rqst *rqs
if (slen > 16 || (len -= (slen + 2)*4) < 0)
goto badcred;
for (i = 0; i < slen; i++)
- if (i < NGROUPS)
+ if (i < SVC_CRED_NGROUPS)
cred->cr_groups[i] = ntohl(svc_getu32(argv));
else
svc_getu32(argv);
- if (i < NGROUPS)
+ if (i < SVC_CRED_NGROUPS)
cred->cr_groups[i] = NOGROUP;

if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {


2003-09-29 17:26:32

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH] Many groups patch.


On Mon, 29 Sep 2003, Rusty Russell wrote:
>
> This version drops the internal groups array (it's so often shared
> that it's not worth it, and the logic becomes a bit neater), and does
> vmalloc fallback in case someone has massive number of groups.

Why?

kmalloc() works fine. Anybody who needs 200 groups may be sane, but
anybody who needs more than fits in a kmalloc() is definitely so far out
that there is no point.

The vmalloc space is limited, and the code just gets uglier.

Have you been looking at glibc sources lately, or why do you believe that
we should encourage insane usage?

Linus

2003-09-29 22:29:38

by Pete Zaitcev

[permalink] [raw]
Subject: Re: [PATCH] Many groups patch.

>> This version drops the internal groups array (it's so often shared
>> that it's not worth it, and the logic becomes a bit neater), and does
>> vmalloc fallback in case someone has massive number of groups.
>
> Why?

> The vmalloc space is limited, and the code just gets uglier.

Tim was going to write a version that segments groups into
smaller arrays. I reckon it was too difficult?

> Have you been looking at glibc sources lately, or why do you believe that
> we should encourage insane usage?

We have some customers who run insane number of groups,
with their own patches. This practice is popular in the
Beowulf crowd for some reason. I should note this is not
very mainstream.

-- Pete

2003-09-29 22:54:04

by Tim Hockin

[permalink] [raw]
Subject: Re: [PATCH] Many groups patch.

On Mon, Sep 29, 2003 at 06:29:32PM -0400, Pete Zaitcev wrote:
> >> This version drops the internal groups array (it's so often shared
> >> that it's not worth it, and the logic becomes a bit neater), and does
> >> vmalloc fallback in case someone has massive number of groups.
> >
> > Why?
>
> > The vmalloc space is limited, and the code just gets uglier.
>
> Tim was going to write a version that segments groups into
> smaller arrays. I reckon it was too difficult?

I posted it once or twice then got busy. It's an array of pages. Rusty has
it, but didn't believe me when I said Linus wouldn't let vmalloc() fly.

> > Have you been looking at glibc sources lately, or why do you believe that
> > we should encourage insane usage?
>
> We have some customers who run insane number of groups,
> with their own patches. This practice is popular in the
> Beowulf crowd for some reason. I should note this is not
> very mainstream.

I'd be ok with the simplest "kmalloc or too bad" version, but our customers
wouldn't.

My version uses a struct group_info which has an array of pages. The groups
are sorted and bsearched, instead of linear. The perfomance is quite good.
An older version against 2.6.0-test1 or something is attached. If this
method will fly, I'll take some of Rusty's good ideas and finish this
version of it..

Linus?

2003-09-29 23:10:33

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH] Many groups patch.


On Mon, 29 Sep 2003, Tim Hockin wrote:
>
> On Mon, Sep 29, 2003 at 03:43:43PM -0700, Tim Hockin wrote:
> > My version uses a struct group_info which has an array of pages. The groups
>
> Woops, patch is here

I'm definitely happier about this one.

Not that I'm any more thrilled about users using thousands of groups. But
this looks a bit saner.

Linus

2003-09-29 23:06:28

by Tim Hockin

[permalink] [raw]
Subject: Re: [PATCH] Many groups patch.

On Mon, Sep 29, 2003 at 03:43:43PM -0700, Tim Hockin wrote:
> My version uses a struct group_info which has an array of pages. The groups

Woops, patch is here


Attachments:
(No filename) (161.00 B)
diff-09-25.diff (28.20 kB)
Download all attachments

2003-09-30 03:31:59

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH] Many groups patch.

In message <[email protected]> you write:
>
> On Mon, 29 Sep 2003, Rusty Russell wrote:
> >
> > This version drops the internal groups array (it's so often shared
> > that it's not worth it, and the logic becomes a bit neater), and does
> > vmalloc fallback in case someone has massive number of groups.
>
> Why?

(Rusty points at Tim).

He has 10,000 groups. Now me, I'm happy with the minimal fix.

> kmalloc() works fine. Anybody who needs 200 groups may be sane, but
> anybody who needs more than fits in a kmalloc() is definitely so far out
> that there is no point.

And worse, there are the intermediate kmallocs which would need to be
fixed (thanks to Stephen Rothwell for pointing this out). Fixing this
would make it even uglier.

Here's an updated one (with David Meybohm's fix, too -- Thanks!),
Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.

Name: Dynamic Allocation of Groups Array When Required: With Refcounting
Author: Rusty Russell
Status: Booted on 2.6.0-test6-bk1
Depends: Misc/qemu-page-offset.patch.gz

D: This patch allows the maximum number of groups to be varied using
D: sysctl. Since sharing is so common, we use a refcounted external
D: array for groups.
D:
D: Changes:
D: 1) Remove the NGROUPS define from archs.
D: 2) Fixup the few places which declare [NGROUPS] arrays on the stack.
D: 3) The ia64, s390 and sparc64 ports have their own setgroups/getgroups
D: implementations: unify them on the ia64 one, which calls the core
D: functions.
D: 4) Change the task_struct's groups to a pointer to inside a refcounted
D: external array, fix up fork() to inc refcount.
D: 5) Introduce max_groups and use it instead of NGROUPS.
D: 6) Add a sysctl to vary max_groups.
D:
D: This patch scars nfs: artificially restrict the groups there to
D: SVC_CRED_NGROUPS (32), which is probably wrong, but won't break if
D: they don't change the default.
D:
D: This patch breaks intermezzo: I'm not sure how they want to deal
D: with it.

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/arch/ia64/ia32/sys_ia32.c .12070-2.6.0-test6-more_groups_refcount/arch/ia64/ia32/sys_ia32.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/arch/ia64/ia32/sys_ia32.c 2003-09-29 10:25:17.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/arch/ia64/ia32/sys_ia32.c 2003-09-29 17:18:46.000000000 +1000
@@ -2432,17 +2432,24 @@ asmlinkage long
sys32_getgroups16 (int gidsetsize, short *grouplist)
{
mm_segment_t old_fs = get_fs();
- gid_t gl[NGROUPS];
+ gid_t *gl;
int ret, i;

+ gl = kmalloc(sizeof(gl[0]) * current->ngroups, GFP_KERNEL);
+ if (!gl)
+ return -ENOMEM;
+
set_fs(KERNEL_DS);
ret = sys_getgroups(gidsetsize, gl);
set_fs(old_fs);

- if (gidsetsize && ret > 0 && ret <= NGROUPS)
+ if (gidsetsize && ret > 0)
for (i = 0; i < ret; i++, grouplist++)
- if (put_user(gl[i], grouplist))
- return -EFAULT;
+ if (put_user(gl[i], grouplist)) {
+ ret = -EFAULT;
+ break;
+ }
+ kfree(gl);
return ret;
}

@@ -2452,17 +2459,23 @@ asmlinkage long
sys32_setgroups16 (int gidsetsize, short *grouplist)
{
mm_segment_t old_fs = get_fs();
- gid_t gl[NGROUPS];
+ gid_t *gl;
int ret, i;

- if ((unsigned) gidsetsize > NGROUPS)
+ if ((unsigned) gidsetsize > max_groups)
return -EINVAL;
+ gl = kmalloc(sizeof(gl[0]) * gidsetsize, GFP_KERNEL);
+ if (!gl)
+ return -ENOMEM;
for (i = 0; i < gidsetsize; i++, grouplist++)
- if (get_user(gl[i], grouplist))
+ if (get_user(gl[i], grouplist)) {
+ kfree(gl);
return -EFAULT;
+ }
set_fs(KERNEL_DS);
ret = sys_setgroups(gidsetsize, gl);
set_fs(old_fs);
+ kfree(gl);
return ret;
}

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/arch/mips/kernel/sysirix.c .12070-2.6.0-test6-more_groups_refcount/arch/mips/kernel/sysirix.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/arch/mips/kernel/sysirix.c 2003-09-29 10:25:18.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/arch/mips/kernel/sysirix.c 2003-09-29 17:18:46.000000000 +1000
@@ -368,7 +368,7 @@ asmlinkage int irix_syssgi(struct pt_reg
retval = HZ;
goto out;
case 4:
- retval = NGROUPS;
+ retval = max_groups;
goto out;
case 5:
retval = NR_OPEN;
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/arch/s390/kernel/compat_linux.c .12070-2.6.0-test6-more_groups_refcount/arch/s390/kernel/compat_linux.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/arch/s390/kernel/compat_linux.c 2003-09-29 10:25:21.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/arch/s390/kernel/compat_linux.c 2003-09-29 17:18:46.000000000 +1000
@@ -189,40 +189,57 @@ asmlinkage long sys32_setfsgid16(u16 gid
return sys_setfsgid((gid_t)gid);
}

-asmlinkage long sys32_getgroups16(int gidsetsize, u16 *grouplist)
+extern asmlinkage long sys_getgroups (int gidsetsize, gid_t *grouplist);
+
+asmlinkage long
+sys32_getgroups16 (int gidsetsize, short *grouplist)
{
- u16 groups[NGROUPS];
- int i,j;
+ mm_segment_t old_fs = get_fs();
+ gid_t *gl;
+ int ret, i;

- if (gidsetsize < 0)
- return -EINVAL;
- i = current->ngroups;
- if (gidsetsize) {
- if (i > gidsetsize)
- return -EINVAL;
- for(j=0;j<i;j++)
- groups[j] = current->groups[j];
- if (copy_to_user(grouplist, groups, sizeof(u16)*i))
- return -EFAULT;
- }
- return i;
+ gl = kmalloc(sizeof(gl[0]) * current->ngroups, GFP_KERNEL);
+ if (!gl)
+ return -ENOMEM;
+
+ set_fs(KERNEL_DS);
+ ret = sys_getgroups(gidsetsize, gl);
+ set_fs(old_fs);
+
+ if (gidsetsize && ret > 0)
+ for (i = 0; i < ret; i++, grouplist++)
+ if (put_user(gl[i], grouplist)) {
+ ret = -EFAULT;
+ break;
+ }
+ kfree(gl);
+ return ret;
}

-asmlinkage long sys32_setgroups16(int gidsetsize, u16 *grouplist)
+extern asmlinkage long sys_setgroups (int gidsetsize, gid_t *grouplist);
+
+asmlinkage long
+sys32_setgroups16 (int gidsetsize, short *grouplist)
{
- u16 groups[NGROUPS];
- int i;
+ mm_segment_t old_fs = get_fs();
+ gid_t *gl;
+ int ret, i;

- if (!capable(CAP_SETGID))
- return -EPERM;
- if ((unsigned) gidsetsize > NGROUPS)
+ if ((unsigned) gidsetsize > max_groups)
return -EINVAL;
- if (copy_from_user(groups, grouplist, gidsetsize * sizeof(u16)))
- return -EFAULT;
- for (i = 0 ; i < gidsetsize ; i++)
- current->groups[i] = (gid_t)groups[i];
- current->ngroups = gidsetsize;
- return 0;
+ gl = kmalloc(sizeof(gl[0]) * gidsetsize, GFP_KERNEL);
+ if (!gl)
+ return -ENOMEM;
+ for (i = 0; i < gidsetsize; i++, grouplist++)
+ if (get_user(gl[i], grouplist)) {
+ kfree(gl);
+ return -EFAULT;
+ }
+ set_fs(KERNEL_DS);
+ ret = sys_setgroups(gidsetsize, gl);
+ set_fs(old_fs);
+ kfree(gl);
+ return ret;
}

asmlinkage long sys32_getuid16(void)
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/arch/sparc/kernel/sys_sunos.c .12070-2.6.0-test6-more_groups_refcount/arch/sparc/kernel/sys_sunos.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/arch/sparc/kernel/sys_sunos.c 2003-09-22 10:27:56.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/arch/sparc/kernel/sys_sunos.c 2003-09-29 17:18:46.000000000 +1000
@@ -896,7 +896,7 @@ extern asmlinkage long sunos_sysconf (in
ret = HZ;
break;
case _SC_NGROUPS_MAX:
- ret = NGROUPS_MAX;
+ ret = max_groups;
break;
case _SC_OPEN_MAX:
ret = OPEN_MAX;
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/arch/sparc64/kernel/sys_sparc32.c .12070-2.6.0-test6-more_groups_refcount/arch/sparc64/kernel/sys_sparc32.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/arch/sparc64/kernel/sys_sparc32.c 2003-09-29 10:25:22.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/arch/sparc64/kernel/sys_sparc32.c 2003-09-29 17:18:46.000000000 +1000
@@ -206,40 +206,57 @@ asmlinkage long sys32_setfsgid16(u16 gid
return sys_setfsgid((gid_t)gid);
}

-asmlinkage long sys32_getgroups16(int gidsetsize, u16 *grouplist)
+extern asmlinkage long sys_getgroups (int gidsetsize, gid_t *grouplist);
+
+asmlinkage long
+sys32_getgroups16 (int gidsetsize, short *grouplist)
{
- u16 groups[NGROUPS];
- int i,j;
+ mm_segment_t old_fs = get_fs();
+ gid_t *gl;
+ int ret, i;

- if (gidsetsize < 0)
- return -EINVAL;
- i = current->ngroups;
- if (gidsetsize) {
- if (i > gidsetsize)
- return -EINVAL;
- for(j=0;j<i;j++)
- groups[j] = current->groups[j];
- if (copy_to_user(grouplist, groups, sizeof(u16)*i))
- return -EFAULT;
- }
- return i;
+ gl = kmalloc(sizeof(gl[0]) * current->ngroups, GFP_KERNEL);
+ if (!gl)
+ return -ENOMEM;
+
+ set_fs(KERNEL_DS);
+ ret = sys_getgroups(gidsetsize, gl);
+ set_fs(old_fs);
+
+ if (gidsetsize && ret > 0)
+ for (i = 0; i < ret; i++, grouplist++)
+ if (put_user(gl[i], grouplist)) {
+ ret = -EFAULT;
+ break;
+ }
+ kfree(gl);
+ return ret;
}

-asmlinkage long sys32_setgroups16(int gidsetsize, u16 *grouplist)
+extern asmlinkage long sys_setgroups (int gidsetsize, gid_t *grouplist);
+
+asmlinkage long
+sys32_setgroups16 (int gidsetsize, short *grouplist)
{
- u16 groups[NGROUPS];
- int i;
+ mm_segment_t old_fs = get_fs();
+ gid_t *gl;
+ int ret, i;

- if (!capable(CAP_SETGID))
- return -EPERM;
- if ((unsigned) gidsetsize > NGROUPS)
+ if ((unsigned) gidsetsize > max_groups)
return -EINVAL;
- if (copy_from_user(groups, grouplist, gidsetsize * sizeof(u16)))
- return -EFAULT;
- for (i = 0 ; i < gidsetsize ; i++)
- current->groups[i] = (gid_t)groups[i];
- current->ngroups = gidsetsize;
- return 0;
+ gl = kmalloc(sizeof(gl[0]) * gidsetsize, GFP_KERNEL);
+ if (!gl)
+ return -ENOMEM;
+ for (i = 0; i < gidsetsize; i++, grouplist++)
+ if (get_user(gl[i], grouplist)) {
+ kfree(gl);
+ return -EFAULT;
+ }
+ set_fs(KERNEL_DS);
+ ret = sys_setgroups(gidsetsize, gl);
+ set_fs(old_fs);
+ kfree(gl);
+ return ret;
}

asmlinkage long sys32_getuid16(void)
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/arch/sparc64/kernel/sys_sunos32.c .12070-2.6.0-test6-more_groups_refcount/arch/sparc64/kernel/sys_sunos32.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/arch/sparc64/kernel/sys_sunos32.c 2003-09-22 10:27:56.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/arch/sparc64/kernel/sys_sunos32.c 2003-09-29 17:18:46.000000000 +1000
@@ -859,7 +859,7 @@ extern asmlinkage s32 sunos_sysconf (int
ret = HZ;
break;
case _SC_NGROUPS_MAX:
- ret = NGROUPS_MAX;
+ ret = max_groups;
break;
case _SC_OPEN_MAX:
ret = OPEN_MAX;
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/fs/nfsd/auth.c .12070-2.6.0-test6-more_groups_refcount/fs/nfsd/auth.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/fs/nfsd/auth.c 2003-09-22 10:21:34.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/fs/nfsd/auth.c 2003-09-29 17:18:46.000000000 +1000
@@ -26,7 +26,7 @@ nfsd_setuser(struct svc_rqst *rqstp, str
cred->cr_uid = exp->ex_anon_uid;
if (!cred->cr_gid)
cred->cr_gid = exp->ex_anon_gid;
- for (i = 0; i < NGROUPS; i++)
+ for (i = 0; i < SVC_CRED_NGROUPS; i++)
if (!cred->cr_groups[i])
cred->cr_groups[i] = exp->ex_anon_gid;
}
@@ -39,7 +39,9 @@ nfsd_setuser(struct svc_rqst *rqstp, str
current->fsgid = cred->cr_gid;
else
current->fsgid = exp->ex_anon_gid;
- for (i = 0; i < NGROUPS; i++) {
+
+ /* We can do this because cow_current_groups() was done at birth. */
+ for (i = 0; i < SVC_CRED_NGROUPS; i++) {
gid_t group = cred->cr_groups[i];
if (group == (gid_t) NOGROUP)
break;
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/fs/nfsd/nfs4state.c .12070-2.6.0-test6-more_groups_refcount/fs/nfsd/nfs4state.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/fs/nfsd/nfs4state.c 2003-09-29 10:25:52.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/fs/nfsd/nfs4state.c 2003-09-29 17:18:46.000000000 +1000
@@ -244,7 +244,7 @@ copy_cred(struct svc_cred *target, struc

target->cr_uid = source->cr_uid;
target->cr_gid = source->cr_gid;
- for(i = 0; i < NGROUPS; i++)
+ for(i = 0; i < SVC_CREDS_NGROUPS; i++)
target->cr_groups[i] = source->cr_groups[i];
}

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/fs/nfsd/nfssvc.c .12070-2.6.0-test6-more_groups_refcount/fs/nfsd/nfssvc.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/fs/nfsd/nfssvc.c 2003-09-22 10:26:12.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/fs/nfsd/nfssvc.c 2003-09-29 17:18:46.000000000 +1000
@@ -182,6 +182,11 @@ nfsd(struct svc_rqst *rqstp)
daemonize("nfsd");
current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;

+ if (cow_current_groups(SVC_CRED_NGROUPS) < 0) {
+ printk("Unable to start nfsd thread: can't set groups\n");
+ goto out;
+ }
+
/* After daemonize() this kernel thread shares current->fs
* with the init process. We need to create files with a
* umask of 0 instead of init's umask. */
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-alpha/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-alpha/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-alpha/param.h 2003-09-21 17:27:17.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-alpha/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -19,10 +19,6 @@

#define EXEC_PAGESIZE 8192

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-arm/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-arm/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-arm/param.h 2003-09-22 10:28:10.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-arm/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -26,10 +26,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-arm26/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-arm26/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-arm26/param.h 2003-09-22 10:09:07.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-arm26/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -22,10 +22,6 @@
# define HZ 100
#endif

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-cris/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-cris/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-cris/param.h 2003-09-22 10:23:13.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-cris/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -14,10 +14,6 @@

#define EXEC_PAGESIZE 8192

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-h8300/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-h8300/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-h8300/param.h 2003-09-22 10:07:04.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-h8300/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -14,10 +14,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-i386/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-i386/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-i386/param.h 2003-09-29 17:18:46.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-i386/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -18,10 +18,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-ia64/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-ia64/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-ia64/param.h 2003-09-29 10:25:58.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-ia64/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -10,10 +10,6 @@

#define EXEC_PAGESIZE 65536

-#ifndef NGROUPS
-# define NGROUPS 32
-#endif
-
#ifndef NOGROUP
# define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-m68k/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-m68k/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-m68k/param.h 2003-09-21 17:26:43.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-m68k/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -13,10 +13,6 @@

#define EXEC_PAGESIZE 8192

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-m68knommu/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-m68knommu/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-m68knommu/param.h 2003-09-21 17:31:31.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-m68knommu/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -44,10 +44,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-mips/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-mips/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-mips/param.h 2003-09-22 10:22:44.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-mips/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -33,10 +33,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-parisc/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-parisc/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-parisc/param.h 2003-09-21 17:31:10.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-parisc/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -17,10 +17,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-ppc/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-ppc/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-ppc/param.h 2003-09-22 09:47:27.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-ppc/param.h 2003-09-29 17:18:46.000000000 +1000
@@ -13,10 +13,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-ppc64/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-ppc64/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-ppc64/param.h 2003-09-21 17:26:44.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-ppc64/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -20,10 +20,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-s390/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-s390/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-s390/param.h 2003-09-21 17:29:29.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-s390/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -21,10 +21,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-sh/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-sh/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-sh/param.h 2003-09-22 10:23:00.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-sh/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -17,10 +17,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-sparc/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-sparc/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-sparc/param.h 2003-09-21 17:26:18.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-sparc/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -14,10 +14,6 @@

#define EXEC_PAGESIZE 8192 /* Thanks for sun4's we carry baggage... */

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-sparc64/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-sparc64/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-sparc64/param.h 2003-09-21 17:26:18.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-sparc64/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -14,10 +14,6 @@

#define EXEC_PAGESIZE 8192 /* Thanks for sun4's we carry baggage... */

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-um/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-um/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-um/param.h 2003-09-21 17:28:16.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-um/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -3,10 +3,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-v850/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-v850/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-v850/param.h 2003-09-21 17:31:32.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-v850/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -18,10 +18,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-x86_64/param.h .12070-2.6.0-test6-more_groups_refcount/include/asm-x86_64/param.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/asm-x86_64/param.h 2003-09-21 17:30:30.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/asm-x86_64/param.h 2003-09-29 17:18:47.000000000 +1000
@@ -13,10 +13,6 @@

#define EXEC_PAGESIZE 4096

-#ifndef NGROUPS
-#define NGROUPS 32
-#endif
-
#ifndef NOGROUP
#define NOGROUP (-1)
#endif
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/init_task.h .12070-2.6.0-test6-more_groups_refcount/include/linux/init_task.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/init_task.h 2003-09-22 10:27:37.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/linux/init_task.h 2003-09-29 17:18:47.000000000 +1000
@@ -56,6 +56,8 @@
.siglock = SPIN_LOCK_UNLOCKED, \
}

+extern struct task_groups init_groups;
+
/*
* INIT_TASK is used to set up the first task table, touch at
* your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -108,6 +110,7 @@
.proc_lock = SPIN_LOCK_UNLOCKED, \
.switch_lock = SPIN_LOCK_UNLOCKED, \
.journal_info = NULL, \
+ .groups = init_groups.groups, \
}


diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/sched.h .12070-2.6.0-test6-more_groups_refcount/include/linux/sched.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/sched.h 2003-09-29 10:26:05.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/linux/sched.h 2003-09-29 17:18:47.000000000 +1000
@@ -328,6 +328,15 @@ struct k_itimer {
struct io_context; /* See blkdev.h */
void exit_io_context(void);

+/* Size is determined by task_struct's ngroups. */
+struct task_groups
+{
+ atomic_t usage;
+ gid_t groups[0];
+};
+
+extern int max_groups;
+
struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
struct thread_info *thread_info;
@@ -403,7 +413,7 @@ struct task_struct {
uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;
int ngroups;
- gid_t groups[NGROUPS];
+ gid_t *groups; /* task_groups->groups */
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
int keep_capabilities:1;
struct user_struct *user;
@@ -602,6 +612,12 @@ extern int send_group_sigqueue(int, stru
extern int do_sigaction(int, const struct k_sigaction *, struct k_sigaction *);
extern int do_sigaltstack(const stack_t __user *, stack_t __user *, unsigned long);

+/* container_of doesn't like arrays. */
+static inline struct task_groups *task_groups(gid_t *groups)
+{
+ return (void *)groups - offsetof(struct task_groups, groups);
+}
+
/* These can be the second arg to send_sig_info/send_group_sig_info. */
#define SEND_SIG_NOINFO ((struct siginfo *) 0)
#define SEND_SIG_PRIV ((struct siginfo *) 1)
@@ -678,6 +694,10 @@ extern int do_execve(char *, char __user
extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *);
extern struct task_struct * copy_process(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *);

+/* Copy-on-write current groups to this size, if possible. 0 or -err. */
+extern int cow_current_groups(unsigned int ngroups);
+extern void release_groups(gid_t *groups);
+
#ifdef CONFIG_SMP
extern void wait_task_inactive(task_t * p);
#else
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/sunrpc/svcauth.h .12070-2.6.0-test6-more_groups_refcount/include/linux/sunrpc/svcauth.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/sunrpc/svcauth.h 2003-09-22 09:47:41.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/linux/sunrpc/svcauth.h 2003-09-29 17:18:47.000000000 +1000
@@ -16,10 +16,12 @@
#include <linux/sunrpc/cache.h>
#include <linux/hash.h>

+#define SVC_CRED_NGROUPS 32
+
struct svc_cred {
uid_t cr_uid;
gid_t cr_gid;
- gid_t cr_groups[NGROUPS];
+ gid_t cr_groups[SVC_CRED_NGROUPS];
};

struct svc_rqst; /* forward decl */
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/sysctl.h .12070-2.6.0-test6-more_groups_refcount/include/linux/sysctl.h
--- .12070-2.6.0-test6-more_groups_refcount.pre/include/linux/sysctl.h 2003-09-22 10:28:13.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/include/linux/sysctl.h 2003-09-29 17:18:47.000000000 +1000
@@ -127,6 +127,7 @@ enum
KERN_PANIC_ON_OOPS=57, /* int: whether we will panic on an oops */
KERN_HPPA_PWRSW=58, /* int: hppa soft-power enable */
KERN_HPPA_UNALIGNED=59, /* int: hppa unaligned-trap enable */
+ KERN_MAX_GROUPS=60, /* int: setgroups limit */
};


diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/init/main.c .12070-2.6.0-test6-more_groups_refcount/init/main.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/init/main.c 2003-09-29 10:26:06.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/init/main.c 2003-09-29 17:18:47.000000000 +1000
@@ -594,7 +594,6 @@ static int init(void * unused)
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
-
if (execute_command)
run_init_process(execute_command);

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/kernel/fork.c .12070-2.6.0-test6-more_groups_refcount/kernel/fork.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/kernel/fork.c 2003-09-29 10:26:06.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/kernel/fork.c 2003-09-29 17:18:47.000000000 +1000
@@ -85,6 +85,7 @@ void __put_task_struct(struct task_struc

security_task_free(tsk);
free_uid(tsk->user);
+ release_groups(tsk->groups);
free_task(tsk);
}

@@ -817,6 +818,7 @@ struct task_struct *copy_process(unsigne

atomic_inc(&p->user->__count);
atomic_inc(&p->user->processes);
+ atomic_inc(&task_groups(p->groups)->usage);

/*
* If multiple threads are within copy_process(), then this check
@@ -1063,6 +1065,7 @@ bad_fork_cleanup_put_domain:
bad_fork_cleanup_count:
atomic_dec(&p->user->processes);
free_uid(p->user);
+ release_groups(p->groups);
bad_fork_free:
free_task(p);
goto fork_out;
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/kernel/sys.c .12070-2.6.0-test6-more_groups_refcount/kernel/sys.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/kernel/sys.c 2003-09-29 10:26:06.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/kernel/sys.c 2003-09-29 17:18:47.000000000 +1000
@@ -1069,6 +1069,56 @@ out:
return err;
}

+/* Never freed */
+struct task_groups init_groups = { .usage = ATOMIC_INIT(2) };
+
+int max_groups = 32;
+
+void release_groups(gid_t *groups)
+{
+ struct task_groups *tgrp = task_groups(groups);
+
+ if (atomic_dec_and_test(&tgrp->usage))
+ kfree(tgrp);
+}
+
+/* This does the actual copy and changeover. Caller sets
+ * current->ngroups and changes group array if everything else goes ok. */
+static inline int alloc_new_groups(unsigned int ngroups)
+{
+ struct task_groups *tgrp;
+
+ if (ngroups < current->ngroups)
+ ngroups = current->ngroups;
+
+ tgrp = kmalloc(sizeof(*tgrp)+sizeof(tgrp->groups[0])*ngroups,GFP_KERNEL);
+ if (!tgrp)
+ return -ENOMEM;
+ atomic_set(&tgrp->usage, 1);
+
+ memcpy(tgrp->groups, current->groups,
+ sizeof(tgrp->groups[0]) * current->ngroups);
+
+ release_groups(current->groups);
+ current->groups = tgrp->groups;
+ return 0;
+}
+
+/* Unshare and maybe enlarge current groups to this size, if possible.
+ * 0 or -err. */
+int cow_current_groups(unsigned int ngroups)
+{
+ if (ngroups > max_groups)
+ return -EINVAL;
+
+ /* Shared, or needs expansion? */
+ if (atomic_read(&task_groups(current->groups)->usage) > 1
+ || ngroups > current->ngroups)
+ return alloc_new_groups(ngroups);
+
+ return 0;
+}
+
/*
* Supplementary group IDs
*/
@@ -1094,27 +1150,36 @@ asmlinkage long sys_getgroups(int gidset
}

/*
- * SMP: Our groups are not shared. We can copy to/from them safely
+ * SMP: Our groups are copy-on-write. We can copy to/from them safely
* without another task interfering.
*/

asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist)
{
- gid_t groups[NGROUPS];
+ gid_t *groups;
int retval;

+ printk("sys_setgroups %u groups\n", gidsetsize);
if (!capable(CAP_SETGID))
return -EPERM;
- if ((unsigned) gidsetsize > NGROUPS)
- return -EINVAL;
- if (copy_from_user(groups, grouplist, gidsetsize * sizeof(gid_t)))
- return -EFAULT;
+ retval = cow_current_groups(gidsetsize);
+ if (retval < 0)
+ return retval;
+ groups = kmalloc(sizeof(groups[0]) * gidsetsize, GFP_KERNEL);
+ if (!groups)
+ return -ENOMEM;
+ if (copy_from_user(groups, grouplist, gidsetsize * sizeof(gid_t))) {
+ retval = -EFAULT;
+ goto out;
+ }
retval = security_task_setgroups(gidsetsize, groups);
if (retval)
- return retval;
+ goto out;
memcpy(current->groups, groups, gidsetsize * sizeof(gid_t));
current->ngroups = gidsetsize;
- return 0;
+out:
+ kfree(groups);
+ return retval;
}

static int supplemental_group_member(gid_t grp)
@@ -1434,6 +1499,7 @@ asmlinkage long sys_prctl(int option, un
return error;
}

+EXPORT_SYMBOL(cow_current_groups);
EXPORT_SYMBOL(notifier_chain_register);
EXPORT_SYMBOL(notifier_chain_unregister);
EXPORT_SYMBOL(notifier_call_chain);
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/kernel/sysctl.c .12070-2.6.0-test6-more_groups_refcount/kernel/sysctl.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/kernel/sysctl.c 2003-09-29 10:26:06.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/kernel/sysctl.c 2003-09-29 17:18:47.000000000 +1000
@@ -581,6 +581,14 @@ static ctl_table kern_table[] = {
.mode = 0644,
.proc_handler = &proc_dointvec,
},
+ {
+ .ctl_name = KERN_MAX_GROUPS,
+ .procname = "max_groups",
+ .data = &max_groups,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
{ .ctl_name = 0 }
};

diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/kernel/uid16.c .12070-2.6.0-test6-more_groups_refcount/kernel/uid16.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/kernel/uid16.c 2003-09-22 10:07:19.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/kernel/uid16.c 2003-09-29 17:18:47.000000000 +1000
@@ -109,7 +109,6 @@ asmlinkage long sys_setfsgid16(old_gid_t

asmlinkage long sys_getgroups16(int gidsetsize, old_gid_t __user *grouplist)
{
- old_gid_t groups[NGROUPS];
int i,j;

if (gidsetsize < 0)
@@ -118,34 +117,48 @@ asmlinkage long sys_getgroups16(int gids
if (gidsetsize) {
if (i > gidsetsize)
return -EINVAL;
- for(j=0;j<i;j++)
- groups[j] = current->groups[j];
- if (copy_to_user(grouplist, groups, sizeof(old_gid_t)*i))
+ if (!access_ok(VERIFY_WRITE, grouplist, sizeof(old_gid_t)*i))
return -EFAULT;
+ for(j=0;j<i;j++) {
+ old_gid_t group;
+ group = current->groups[j];
+ if (copy_to_user(grouplist+j, &group, sizeof(group)))
+ return -EFAULT;
+ }
}
return i;
}

asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t __user *grouplist)
{
- old_gid_t groups[NGROUPS];
- gid_t new_groups[NGROUPS];
- int i;
+ old_gid_t *groups;
+ gid_t *new_groups;
+ int i, ret;

if (!capable(CAP_SETGID))
return -EPERM;
- if ((unsigned) gidsetsize > NGROUPS)
- return -EINVAL;
+
+ if ((ret = cow_current_groups(gidsetsize)) < 0)
+ return ret;
+ ret = -ENOMEM;
+ groups = kmalloc(sizeof(groups[0]) * gidsetsize, GFP_KERNEL);
+ new_groups = kmalloc(sizeof(new_groups[0]) * gidsetsize, GFP_KERNEL);
+ if (!groups || !new_groups)
+ goto out;
+ ret = -EFAULT;
if (copy_from_user(groups, grouplist, gidsetsize * sizeof(old_gid_t)))
- return -EFAULT;
+ goto out;
for (i = 0 ; i < gidsetsize ; i++)
new_groups[i] = (gid_t)groups[i];
- i = security_task_setgroups(gidsetsize, new_groups);
- if (i)
- return i;
+ ret = security_task_setgroups(gidsetsize, new_groups);
+ if (ret)
+ goto out;
memcpy(current->groups, new_groups, gidsetsize * sizeof(gid_t));
current->ngroups = gidsetsize;
- return 0;
+out:
+ kfree(groups);
+ kfree(new_groups);
+ return ret;
}

asmlinkage long sys_getuid16(void)
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .12070-2.6.0-test6-more_groups_refcount.pre/net/sunrpc/svcauth_unix.c .12070-2.6.0-test6-more_groups_refcount/net/sunrpc/svcauth_unix.c
--- .12070-2.6.0-test6-more_groups_refcount.pre/net/sunrpc/svcauth_unix.c 2003-09-22 10:23:05.000000000 +1000
+++ .12070-2.6.0-test6-more_groups_refcount/net/sunrpc/svcauth_unix.c 2003-09-29 17:18:47.000000000 +1000
@@ -434,11 +434,11 @@ svcauth_unix_accept(struct svc_rqst *rqs
if (slen > 16 || (len -= (slen + 2)*4) < 0)
goto badcred;
for (i = 0; i < slen; i++)
- if (i < NGROUPS)
+ if (i < SVC_CRED_NGROUPS)
cred->cr_groups[i] = ntohl(svc_getu32(argv));
else
svc_getu32(argv);
- if (i < NGROUPS)
+ if (i < SVC_CRED_NGROUPS)
cred->cr_groups[i] = NOGROUP;

if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {

2003-09-30 04:22:25

by Tim Hockin

[permalink] [raw]
Subject: Re: [PATCH] Many groups patch.

On Tue, Sep 30, 2003 at 09:30:07AM +1000, Rusty Russell wrote:
> > Why?
>
> (Rusty points at Tim).
>
> He has 10,000 groups. Now me, I'm happy with the minimal fix.

I'm going to merge your thoughts and mine tomorrow and send it out. Linus
suggested the array of pages approah is more sane, so I'm going to try for
it. I'm going to comb through the diffs between your patch and mine.

> And worse, there are the intermediate kmallocs which would need to be
> fixed (thanks to Stephen Rothwell for pointing this out). Fixing this
> would make it even uglier.

Specifically? I think my patch gets all of those. At least all the ones I
found.

> Here's an updated one (with David Meybohm's fix, too -- Thanks!),
> Rusty.

Can you elaborate on what this extra fix is?