From 87e0a3ccde14719bc82b069e2018a8a31efcae77 Mon Sep 17 00:00:00 2001
From: Mark Vitale <mvitale@sinenomine.net>
Date: Wed, 8 Jul 2015 14:28:50 -0400
Subject: [PATCH 5/7] Solaris: setpag should verify that ngroups will not overflow

Our ngroups management (since PAGs are still encoded as 2 groups) needs
to ensure that we do not overflow what we are prepared to handle,
and do not panic due to misheld mutexes if we have to return an error
when handling it.

FIXES 131878 (CVE-2015-3286)

(cherry-picked from commit bff4b8c9416053a1a3eb0ed5d6580675acfe3ce7)

Change-Id: I34a298ab776f69d52c8f621f79aafc79199f9cc4
---
 src/afs/SOLARIS/osi_groups.c |   27 ++++++++++++++++++---------
 1 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/src/afs/SOLARIS/osi_groups.c b/src/afs/SOLARIS/osi_groups.c
index 58c510e..321e0c2 100644
--- a/src/afs/SOLARIS/osi_groups.c
+++ b/src/afs/SOLARIS/osi_groups.c
@@ -88,19 +88,28 @@ setpag(cred, pagvalue, newpag, change_parent)
     gid_t *gidset;
     int ngroups, code;
     int j;
+    size_t gidset_sz;
 
     AFS_STATCNT(setpag);
 
-    gidset = (gid_t *) osi_AllocSmallSpace(AFS_SMALLOCSIZ);
+    /* Derive gidset size from running kernel's ngroups_max;
+     * default 16, but configurable up to 32 (Sol10) or
+     * 1024 (Sol11).
+     */
+    gidset_sz = sizeof(gidset[0]) * ngroups_max;
+
+    /* must use osi_Alloc, osi_AllocSmallSpace may not be enough. */
+    gidset = osi_Alloc(gidset_sz);
 
     mutex_enter(&curproc->p_crlock);
     ngroups = afs_getgroups(*cred, gidset);
 
     if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
 	/* We will have to shift grouplist to make room for pag */
-	if ((sizeof gidset[0]) * (ngroups + 2) > AFS_SMALLOCSIZ) {
-	    osi_FreeSmallSpace((char *)gidset);
-	    return (E2BIG);
+	if ((sizeof gidset[0]) * (ngroups + 2) > gidset_sz) {
+	    mutex_exit(&curproc->p_crlock);
+	    code = E2BIG;
+	    goto done;
 	}
 	for (j = ngroups - 1; j >= 0; j--) {
 	    gidset[j + 2] = gidset[j];
@@ -110,11 +119,11 @@ setpag(cred, pagvalue, newpag, change_parent)
     *newpag = (pagvalue == -1 ? genpag() : pagvalue);
     afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
     /* afs_setgroups will release curproc->p_crlock */
-    if (code = afs_setgroups(cred, ngroups, gidset, change_parent)) {
-	osi_FreeSmallSpace((char *)gidset);
-	return (code);
-    }
-    osi_FreeSmallSpace((char *)gidset);
+    /* exit action is same regardless of code */
+    code = afs_setgroups(cred, ngroups, gidset, change_parent);
+
+ done:
+    osi_Free((char *)gidset, gidset_sz);
     return code;
 }
 
-- 
1.7.1

