The branch main has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=09f2abaa59f948e2d21604b5e528a264a6d8c329
commit 09f2abaa59f948e2d21604b5e528a264a6d8c329 Author: Kyle Evans <kev...@freebsd.org> AuthorDate: 2025-08-01 00:50:47 +0000 Commit: Kyle Evans <kev...@freebsd.org> CommitDate: 2025-08-01 00:50:47 +0000 kern: add a new ucred flag for groups having been set Now that we can legitimately have ngroups == 0 as a result of calling crsetgroups(), set a flag when we've set groups for the sake of sanity checking usage of crextend(). While it's true this flag will only really be used under INVARIANTS, it's only the second flag bit that we're adding in 16 years. Reviewed by: olce Differential Revision: https://reviews.freebsd.org/D51646 --- sys/compat/linux/linux_misc.c | 1 + sys/compat/linux/linux_uid16.c | 1 + sys/kern/kern_prot.c | 15 +++++++++++---- sys/sys/ucred.h | 1 + 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c index 31460819e6ab..5e32353c6b8e 100644 --- a/sys/compat/linux/linux_misc.c +++ b/sys/compat/linux/linux_misc.c @@ -1056,6 +1056,7 @@ linux_setgroups(struct thread *td, struct linux_setgroups_args *args) newcred->cr_ngroups = ngrp; for (int i = 0; i < ngrp; i++) newcred->cr_groups[i] = linux_gidset[i]; + newcred->cr_flags |= CRED_FLAG_GROUPSET; setsugid(p); proc_set_cred(p, newcred); diff --git a/sys/compat/linux/linux_uid16.c b/sys/compat/linux/linux_uid16.c index 9fe799c0b9de..1d9a19916412 100644 --- a/sys/compat/linux/linux_uid16.c +++ b/sys/compat/linux/linux_uid16.c @@ -117,6 +117,7 @@ linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args) newcred->cr_ngroups = ngrp; for (int i = 0; i < ngrp; i++) newcred->cr_groups[i] = linux_gidset[i]; + newcred->cr_flags |= CRED_FLAG_GROUPSET; setsugid(td->td_proc); proc_set_cred(p, newcred); diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c index 48ab7c0b520b..632be229af5b 100644 --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -2796,7 +2796,8 @@ crextend(struct ucred *cr, int n) size_t nbytes; MPASS2(cr->cr_ref == 1, "'cr_ref' must be 1 (referenced, unshared)"); - MPASS2(cr->cr_ngroups == 0, "groups on 'cr' already set!"); + MPASS2((cr->cr_flags & CRED_FLAG_GROUPSET) == 0, + "groups on 'cr' already set!"); groups_check_positive_len(n); groups_check_max_len(n); @@ -2890,6 +2891,7 @@ crsetgroups_internal(struct ucred *cr, int ngrp, const gid_t *groups) bcopy(groups, cr->cr_groups, ngrp * sizeof(gid_t)); cr->cr_ngroups = ngrp; + cr->cr_flags |= CRED_FLAG_GROUPSET; } /* @@ -2906,15 +2908,19 @@ crsetgroups(struct ucred *cr, int ngrp, const gid_t *groups) if (ngrp > ngroups_max) ngrp = ngroups_max; + cr->cr_ngroups = 0; + if (ngrp == 0) { + cr->cr_flags |= CRED_FLAG_GROUPSET; + return; + } + /* * crextend() asserts that groups are not set, as it may allocate a new * backing storage without copying the content of the old one. Since we * are going to install a completely new set anyway, signal that we * consider the old ones thrown away. */ - cr->cr_ngroups = 0; - if (ngrp == 0) - return; + cr->cr_flags &= ~CRED_FLAG_GROUPSET; crextend(cr, ngrp); crsetgroups_internal(cr, ngrp, groups); @@ -2936,6 +2942,7 @@ crsetgroups_fallback(struct ucred *cr, int ngrp, const gid_t *groups, if (ngrp == 0) { cr->cr_gid = fallback; cr->cr_ngroups = 0; + cr->cr_flags |= CRED_FLAG_GROUPSET; return; } diff --git a/sys/sys/ucred.h b/sys/sys/ucred.h index 4831d8cb6e1b..cd4efcb71c0d 100644 --- a/sys/sys/ucred.h +++ b/sys/sys/ucred.h @@ -44,6 +44,7 @@ * Flags for cr_flags. */ #define CRED_FLAG_CAPMODE 0x00000001 /* In capability mode. */ +#define CRED_FLAG_GROUPSET 0x00000002 /* Groups have been set. */ /* * Number of groups inlined in 'struct ucred'. It must stay reasonably low as