getgrouplist(3) is not thread-safe

Bug #1923738 reported by Olaf Seibert
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
glibc (Ubuntu)
New
Undecided
Unassigned

Bug Description

Hello glibc people!

While investigating some threading-related problem in a third-party program, I discovered that this program calls getgrouplist(3) from multiple threads, assuming that this is thread-safe. Indeed, the the man page claims that it is:

       ┌───────────────┬───────────────┬────────────────
       │Interface │ Attribute │ Value │
       ├───────────────┼───────────────┼────────────────
       │getgrouplist() │ Thread safety │ MT-Safe locale │
       └───────────────┴───────────────┴────────────────

However, consider the stack traces below.

[Switching to Thread 0x7f691c5ff700 (LWP 30714)]

Thread 353 "mount.quobyte" hit Breakpoint 10, 0x00007f694e7eb3c0 in _nss_extrausers_setgrent () from /usr/lib/libnss_extrausers.so.2
(gdb) bt
#0 0x00007f694e7eb3c0 in _nss_extrausers_setgrent () from /usr/lib/libnss_extrausers.so.2
#1 0x00007f695c72750a in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#2 0x00007f695c72793e in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#3 0x00007f695c727ab1 in getgrouplist () from /lib/x86_64-linux-gnu/libc.so.6
[ remaining frames are from the third-party program - omitted ]

(gdb) c
Continuing.
[Switching to Thread 0x7f68971ff700 (LWP 30934)]

Thread 375 "mount.quobyte" hit Breakpoint 12, 0x00007f694e7eb460 in _nss_extrausers_getgrent_r () from /usr/lib/libnss_extrausers.so.2
(gdb) bt
#0 0x00007f694e7eb460 in _nss_extrausers_getgrent_r () from /usr/lib/libnss_extrausers.so.2
#1 0x00007f695c7275ad in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#2 0x00007f695c72793e in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#3 0x00007f695c727ab1 in getgrouplist () from /lib/x86_64-linux-gnu/libc.so.6
[ remaining frames are from the third-party program - omitted ]

getgrouplist calls _nss_*_setgrent() and _nss_*_getgrent_r(), which are not thread safe. These functions can't be, since their signature has no way to store the FILE* or other reference to the group file that they have to keep between calls. Seeing these functions here should ring some alarm bells.

For example, this is from libnss-extrausers, showing the thead-unsafe function signature and the global variable groupsfile it uses. This signature is forced on it from NSS.

enum nss_status _nss_extrausers_setgrent(void) {
        enum nss_status status = NSS_STATUS_SUCCESS;

        if (groupsfile == NULL) {
                groupsfile = fopen(GROUPSFILE, "re");
                if (groupsfile == NULL)
                        status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
        } else {
                rewind(groupsfile);
        }

        return status;
}

I looked at glibc/grp/initgroups.c, where getgrouplist() is defined. I didn't find proof of locking there. The stack traces above are from Bionic, but the sources I examined didn't seem to differ materially between versions in this area.

ii libc-bin 2.27-3ubuntu1.4 amd64 GNU C Library: Binaries
ii libc-dev-bin 2.27-3ubuntu1.4 amd64 GNU C Library: Development binaries
ii libc6:amd64 2.27-3ubuntu1.4 amd64 GNU C Library: Shared libraries
ii libc6-dev:amd64 2.27-3ubuntu1.4 amd64 GNU C Library: Development Libraries and Header Files

So I can only conclude that getgrouplist(3) is not thread-safe, despite documentation.

Revision history for this message
Olaf Seibert (oseibert-sys11) wrote :
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.