summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2019-10-31 20:27:34 +0100
committerLennart Poettering <lennart@poettering.net>2019-10-31 20:59:27 +0100
commit6e0a3888541d08e34f02ccaf2a86940d266c9f01 (patch)
treef82f2aec356e1439b9a1bc61bfa6d2ca4f25d835
parent3ec56e53a2f83f07ffdd10343ace6d604717c1f0 (diff)
downloadsystemd-6e0a3888541d08e34f02ccaf2a86940d266c9f01.tar.gz
user-util: tweak to in_gid()
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
-rw-r--r--src/basic/user-util.c50
1 files changed, 34 insertions, 16 deletions
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index 7a31a69e36..b5fdfafd61 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -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) {