From a15329798c52c57cc16fc24265327d8b1c73ab41 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Fri, 22 Feb 2008 10:01:36 +0100 Subject: id: avoid race when a group is added between getgrouplist calls * gl/lib/mgetgroups.c (mgetgroups) [N_GROUPS_INIT]: Rename enum. Use a larger value. Update *groups only upon success. Iterate upon failed getgrouplist. --- gl/lib/mgetgroups.c | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) (limited to 'gl') diff --git a/gl/lib/mgetgroups.c b/gl/lib/mgetgroups.c index b63436abf..ba8818e0d 100644 --- a/gl/lib/mgetgroups.c +++ b/gl/lib/mgetgroups.c @@ -26,7 +26,7 @@ #include #include #if HAVE_GETGROUPLIST -#include +# include #endif #include "getugroups.h" #include "xalloc.h" @@ -70,30 +70,47 @@ mgetgroups (char const *username, gid_t gid, GETGROUPS_T **groups) Therefore our usage here just avoids a zero sized buffer. */ if (username) { - enum { INITIAL_GROUP_BUFSIZE = 1u }; - /* INITIAL_GROUP_BUFSIZE is initially small to ensure good test coverage */ - GETGROUPS_T smallbuf[INITIAL_GROUP_BUFSIZE]; + enum { N_GROUPS_INIT = 10 }; + GETGROUPS_T smallbuf[N_GROUPS_INIT]; - max_n_groups = INITIAL_GROUP_BUFSIZE; + max_n_groups = N_GROUPS_INIT; ng = getgrouplist (username, gid, smallbuf, &max_n_groups); g = allocate_groupbuf (max_n_groups); if (g == NULL) return -1; - *groups = g; - if (INITIAL_GROUP_BUFSIZE < max_n_groups) - { - return getgrouplist (username, gid, g, &max_n_groups); - /* XXX: Ignoring the race with group size increase */ - } - else + if (max_n_groups <= N_GROUPS_INIT) { /* smallbuf was big enough, so we already have our data */ memcpy (g, smallbuf, max_n_groups * sizeof *g); - return 0; + *groups = g; + return max_n_groups; + } + + while (1) + { + GETGROUPS_T *h; + ng = getgrouplist (username, gid, g, &max_n_groups); + if (0 <= ng) + { + *groups = g; + return ng; + } + + /* When getgrouplist fails, it guarantees that + max_n_groups reflects the new number of groups. */ + + if (xalloc_oversized (max_n_groups, sizeof *h) + || (h = realloc (g, max_n_groups * sizeof *h) == NULL)) + { + int saved_errno = errno; + free (g); + errno = saved_errno; + return -1; + } + g = h; } - /* getgrouplist failed, fall through and use getugroups instead. */ } /* else no username, so fall through and use getgroups. */ #endif -- cgit v1.2.3-70-g09d2