Bug 24696 - endgrent() clobbers errno=ERRNO for 'group: db files' entry in /etc/nsswitch.conf
Summary: endgrent() clobbers errno=ERRNO for 'group: db files' entry in /etc/nsswitch....
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: nss (show other bugs)
Version: 2.29
: P2 normal
Target Milestone: 2.30
Assignee: dj@redhat.com
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-06-17 21:26 UTC by Sergei Trofimovich
Modified: 2019-07-10 18:56 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments
a.c (304 bytes, text/x-csrc)
2019-06-17 21:29 UTC, Sergei Trofimovich
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Sergei Trofimovich 2019-06-17 21:26:10 UTC
The bug is originally reported by Guillermo D. H. at https://bugs.gentoo.org/688246.

Here is the reproducer:

$ cat /etc/nsswitch.conf
    passwd:         files
    #group:         files
    #https://bugs.gentoo.org/688246
    group:          db files
    initgroups:     files
    shadow:         files
    gshadow:        files
    hosts:          files dns
    networks:       files dns
    protocols:      db files
    services:       db files
    ethers:         db files
    rpc:            db files
    netgroup:       db files

$ cat a.c
    #include <errno.h>
    #include <stdio.h>
    #include <string.h>
    #include <grp.h>
    int main() {
     int saved_errno;
     for (;;) {
      errno = 0;
      struct group *gr = getgrent();
      saved_errno = errno;
      if (!gr)
       break;
     }
     printf("(errno = %s)\n", strerror(saved_errno));
     printf("errno before endgrent() = %s\n", strerror(errno));
     endgrent();
     printf("errno after endgrent() = %s\n", strerror(errno));
    }

$ gcc a.c -o a
$ ./a 
    (errno = Success)
    errno before endgrent() = Success
    errno after endgrent() = Invalid argument <- the bug

The following workaround seems to be enough:

--- a/nss/nss_db/db-open.c
+++ b/nss/nss_db/db-open.c
@@ -63,5 +63,9 @@ internal_setent (const char *file, struct nss_db_map *mapping)
 void
 internal_endent (struct nss_db_map *mapping)
 {
-  munmap (mapping->header, mapping->len);
+  /* Avoid clobbering errno if 'header' was never allocated.  */
+  if (mapping->header != NULL)
+    {
+      munmap (mapping->header, mapping->len);
+    }
 }
Comment 1 Sergei Trofimovich 2019-06-17 21:29:53 UTC
Created attachment 11843 [details]
a.c
Comment 2 Andreas Schwab 2019-07-03 09:28:55 UTC
Any library function is allowed to modify errno unless explicitly documented otherwise.
Comment 3 Florian Weimer 2019-07-03 09:45:59 UTC
Yes, I agree that this is not a bug.
Comment 4 Andreas Schwab 2019-07-03 10:03:34 UTC
Actually, endgrent *is* one of those exceptions:

  The setgrent() and endgrent() functions shall not change the setting of errno if   
  successful.
Comment 5 cvs-commit@gcc.gnu.org 2019-07-10 18:52:24 UTC
The master branch has been updated by DJ Delorie <dj@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=99135114ba23c3110b7e4e650fabdc5e639746b7

commit 99135114ba23c3110b7e4e650fabdc5e639746b7
Author: DJ Delorie <dj@redhat.com>
Date:   Fri Jun 28 18:30:00 2019 -0500

    nss_db: fix endent wrt NULL mappings [BZ #24695] [BZ #24696]
    
    nss_db allows for getpwent et al to be called without a set*ent,
    but it only works once.  After the last get*ent a set*ent is
    required to restart, because the end*ent did not properly reset
    the module.  Resetting it to NULL allows for a proper restart.
    
    If the database doesn't exist, however, end*ent erroniously called
    munmap which set errno.
    
    The test case runs "makedb" inside the testroot, so needs selinux
    DSOs installed.
Comment 6 dj@redhat.com 2019-07-10 18:56:48 UTC
Fix committed.