]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
user-util: tweak to in_gid()
authorLennart Poettering <lennart@poettering.net>
Thu, 31 Oct 2019 19:27:34 +0000 (20:27 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 31 Oct 2019 19:59:27 +0000 (20:59 +0100)
Let's make this robust towards parallel updates to group lists. This is
not going to happen IRL, but it makes me sleep better at night: let's
iterate a couple of times in case the list is updated while we are at
it.

Follow-up for: f5e0b942af1e86993c21f4e5c84342bb10403dac

src/basic/user-util.c

index 7a31a69e360828843500cdd2c7c3548b2c126598..b5fdfafd612f4e14b7b88a6e86d24e21baf65fb9 100644 (file)
@@ -409,8 +409,10 @@ char* gid_to_name(gid_t gid) {
 }
 
 int in_gid(gid_t gid) {
-        gid_t *gids;
-        int ngroups, r, i;
+        _cleanup_free_ gid_t *allocated = NULL;
+        gid_t local[16], *p = local;
+        int ngroups = ELEMENTSOF(local);
+        unsigned attempt = 0;
 
         if (getgid() == gid)
                 return 1;
@@ -421,23 +423,39 @@ int in_gid(gid_t gid) {
         if (!gid_is_valid(gid))
                 return -EINVAL;
 
-        ngroups = getgroups(0, NULL);
-        if (ngroups < 0)
-                return -errno;
-        if (ngroups == 0)
-                return 0;
-
-        gids = newa(gid_t, ngroups);
+        for (;;) {
+                ngroups = getgroups(ngroups, p);
+                if (ngroups >= 0)
+                        break;
+                if (errno != EINVAL)
+                        return -errno;
+
+                /* Give up eventually */
+                if (attempt++ > 10)
+                        return -EINVAL;
+
+                /* Get actual size needed, and size the array explicitly. Note that this is potentially racy
+                 * to use (in multi-threaded programs), hence let's call this in a loop. */
+                ngroups = getgroups(0, NULL);
+                if (ngroups < 0)
+                        return -errno;
+                if (ngroups == 0)
+                        return false;
+
+                free(allocated);
+
+                allocated = new(gid_t, ngroups);
+                if (!allocated)
+                        return -ENOMEM;
 
-        r = getgroups(ngroups, gids);
-        if (r < 0)
-                return -errno;
+                p = allocated;
+        }
 
-        for (i = 0; i < r; i++)
-                if (gids[i] == gid)
-                        return 1;
+        for (int i = 0; i < ngroups; i++)
+                if (p[i] == gid)
+                        return true;
 
-        return 0;
+        return false;
 }
 
 int in_group(const char *name) {