id: avoid race when a group is added between getgrouplist calls
authorJim Meyering <meyering@redhat.com>
Fri, 22 Feb 2008 09:01:36 +0000 (10:01 +0100)
committerJim Meyering <meyering@redhat.com>
Fri, 22 Feb 2008 19:47:54 +0000 (20:47 +0100)
* 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

index b63436a..ba8818e 100644 (file)
@@ -26,7 +26,7 @@
 #include <string.h>
 #include <errno.h>
 #if HAVE_GETGROUPLIST
-#include <grp.h>
+# include <grp.h>
 #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