Index: sys/kern/kern_prot.c =================================================================== --- sys/kern/kern_prot.c +++ sys/kern/kern_prot.c @@ -89,6 +89,8 @@ static void crfree_final(struct ucred *cr); static void crsetgroups_locked(struct ucred *cr, int ngrp, gid_t *groups); +static void crsortgroups(struct ucred *cr); +static void crcopy_withoutgroups(struct ucred *dest, struct ucred *src); #ifndef _SYS_SYSPROTO_H_ struct getpid_args { @@ -832,7 +834,8 @@ newcred = crget(); crextend(newcred, ngrp); PROC_LOCK(p); - oldcred = crcopysafe(p, newcred); + oldcred = p->p_ucred; + crcopy_withoutgroups(newcred, oldcred); #ifdef MAC error = mac_cred_check_setgroups(oldcred, ngrp, groups); @@ -854,6 +857,7 @@ newcred->cr_ngroups = 1; } else { crsetgroups_locked(newcred, ngrp, groups); + crsortgroups(newcred); } setsugid(p); proc_set_cred(p, newcred); @@ -2095,17 +2099,31 @@ */ void crcopy(struct ucred *dest, struct ucred *src) +{ + + KASSERT(dest->cr_ref == 1, ("crcopy of shared ucred")); + crextend(dest, src->cr_ngroups); + crcopy_withoutgroups(dest, src); + crsetgroups_locked(dest, src->cr_ngroups, src->cr_groups); +} + +static void +crcopy_withoutgroups(struct ucred *dest, struct ucred *src) { KASSERT(dest->cr_ref == 1, ("crcopy of shared ucred")); bcopy(&src->cr_startcopy, &dest->cr_startcopy, (unsigned)((caddr_t)&src->cr_endcopy - (caddr_t)&src->cr_startcopy)); - crsetgroups(dest, src->cr_ngroups, src->cr_groups); uihold(dest->cr_uidinfo); uihold(dest->cr_ruidinfo); prison_hold(dest->cr_prison); loginclass_hold(dest->cr_loginclass); + /* + * Preserve the egid + */ + dest->cr_ngroups = 1; + dest->cr_groups[0] = src->cr_groups[0]; #ifdef AUDIT audit_cred_copy(src, dest); #endif @@ -2275,32 +2293,36 @@ } /* - * Copy groups in to a credential, preserving any necessary invariants. - * Currently this includes the sorting of all supplemental gids. + * Copy groups in to a credential, _does_not_ sort groups. * crextend() must have been called before hand to ensure sufficient * space is available. */ static void crsetgroups_locked(struct ucred *cr, int ngrp, gid_t *groups) { - int i; - int j; - gid_t g; - KASSERT(cr->cr_agroups >= ngrp, ("cr_ngroups is too small")); bcopy(groups, cr->cr_groups, ngrp * sizeof(gid_t)); cr->cr_ngroups = ngrp; +} + +/* + * Sort all groups except cr_groups[0] to allow groupmember to + * perform a binary search. + */ +static void +crsortgroups(struct ucred *cr) +{ + int i; + int j; + gid_t g; /* - * Sort all groups except cr_groups[0] to allow groupmember to - * perform a binary search. - * * XXX: If large numbers of groups become common this should * be replaced with shell sort like linux uses or possibly * heap sort. */ - for (i = 2; i < ngrp; i++) { + for (i = 2; i < cr->cr_ngroups; i++) { g = cr->cr_groups[i]; for (j = i-1; j >= 1 && g < cr->cr_groups[j]; j--) cr->cr_groups[j + 1] = cr->cr_groups[j]; @@ -2309,7 +2331,9 @@ } /* - * Copy groups in to a credential after expanding it if required. + * Copy groups in to a credential after expanding it if required, + * preserving any necessary invariants. + * Currently this includes the sorting of all supplemental gids. * Truncate the list to (ngroups_max + 1) if it is too large. */ void @@ -2321,6 +2345,7 @@ crextend(cr, ngrp); crsetgroups_locked(cr, ngrp, groups); + crsortgroups(cr); } /*