Re: [PATCH] getugroups: Add _GL_ATTRIBUTE_NONNULL_IF_NONZERO for grouplist

Bruno Haible via Gnulib discussion list <[email protected]>
Newsgroups gmane.comp.lib.gnulib.bugs
Message-ID <18467224.urKdBgtgGi@cagnes>
Paul Eggert wrote:
> > -                if (grouplist && grouplist[n] == grp->gr_gid)
> > +                if (maxcount && grouplist[n] == grp->gr_gid)
> 
> That change isn't needed for correctness, though, right? And the "-" line looks a bit more efficient it's likely to need fewer registers.

When you start talking about efficiency, I wonder why the
grouplist != NULL test is actually present inside the loop.

The git history shows that it was added in commit
94076e90071e99e49b2bc751a86a9d330dd16242 on 1995-05-15,
as a fix for a NULL access when the function is called
with arguments
  maxcount == 0, grouplist == NULL, gid != -1.

Which leads to the question: When the function is specified
to "return the number of IDs we've written into GROUPLIST",
why does it not return 0 in this case right away?

The answer is that the caller (in lib/mgetgroups.c) expects the
return value to be the needed size of the array (like in the Windows
APIs that Sergey mentioned).

Fixed as follows:


2026-08-21  Bruno Haible  <[email protected]>

	getugroups: Clarify and optimize.
	* lib/getugroups.c (getugroups): Fix specification of return value.
	Move grouplist != NULL test out of the inner loop.
	* modules/getugroups (Depends-on): Add bool.

diff --git a/lib/getugroups.c b/lib/getugroups.c
index 6acb583fd9..6c86777046 100644
--- a/lib/getugroups.c
+++ b/lib/getugroups.c
@@ -52,8 +52,12 @@ getugroups (_GL_UNUSED int maxcount,
    process.  Store at most MAXCOUNT group IDs in the GROUPLIST array.
    If GID is not -1, store it first (if possible).  GID should be the
    group ID (pw_gid) obtained from getpwuid, in case USERNAME is not
-   listed in /etc/groups.  Upon failure, set errno and return -1.
-   Otherwise, return the number of IDs we've written into GROUPLIST.  */
+   listed in /etc/groups.
+   Upon failure, set errno and return -1.  Otherwise:
+   If MAXCOUNT is positive, return the number of IDs we've written into
+   GROUPLIST (necessarily <= MAXCOUNT).
+   If MAXCOUNT is zero, return the number of IDs we would have written
+   if GROUPLIST were non-NULL and MAXCOUNT were large enough.  */
 
 int
 getugroups (int maxcount, gid_t *grouplist, char const *username,
@@ -81,13 +85,19 @@ getugroups (int maxcount, gid_t *grouplist, char const *username,
           if (streq (username, *cp))
             {
               /* See if this group number is already on the list.  */
-              int n;
-              for (n = 0; n < count; ++n)
-                if (grouplist && grouplist[n] == grp->gr_gid)
-                  break;
+              bool already_in_grouplist = false;
+              if (maxcount != 0)
+                {
+                  for (int n = 0; n < count; ++n)
+                    if (grouplist[n] == grp->gr_gid)
+                      {
+                        already_in_grouplist = true;
+                        break;
+                      }
+                }
 
               /* If it's a new group number, then try to add it to the list.  */
-              if (n == count)
+              if (!already_in_grouplist)
                 {
                   if (maxcount != 0)
                     {
diff --git a/modules/getugroups b/modules/getugroups
index 0072fac171..af9b3ab614 100644
--- a/modules/getugroups
+++ b/modules/getugroups
@@ -9,6 +9,7 @@ m4/getugroups.m4
 Depends-on:
 errno-h
 streq
+bool
 
 configure.ac:
 gl_GETUGROUPS
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.