diff options
author | Jim Meyering <meyering@redhat.com> | 2008-02-22 10:01:36 +0100 |
---|---|---|
committer | Jim Meyering <meyering@redhat.com> | 2008-02-22 20:47:54 +0100 |
commit | a15329798c52c57cc16fc24265327d8b1c73ab41 (patch) | |
tree | 7ea3f66096ded4274a25d703f9cc29e8b5d9987d /gl/lib | |
parent | 49f7ebaac45f4d20a70c83c8302444b64259c6d3 (diff) | |
download | coreutils-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.
Diffstat (limited to 'gl/lib')
-rw-r--r-- | gl/lib/mgetgroups.c | 45 |
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 |