summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Meyering <meyering@redhat.com>2008-02-22 10:01:36 +0100
committerJim Meyering <meyering@redhat.com>2008-02-22 20:47:54 +0100
commita15329798c52c57cc16fc24265327d8b1c73ab41 (patch)
tree7ea3f66096ded4274a25d703f9cc29e8b5d9987d
parent49f7ebaac45f4d20a70c83c8302444b64259c6d3 (diff)
downloadcoreutils-a15329798c52c57cc16fc24265327d8b1c73ab41.tar.xz
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.
-rw-r--r--gl/lib/mgetgroups.c45
1 files changed, 31 insertions, 14 deletions
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 <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