Created attachment 119596 [details] getpwuid_r.c
glibc-2.3.4-2.9 When using getpwuid_r in a multi-threaded application, invalid reads are performed by __nscd_get_map_ref(). Single threaded and multi-threaded testcases attached.
Created attachment 119597 [details] getpwuid_r_mt.c
This is the valgrind output with the multi-threaded testcase. The single threaded test case works fine in all cases. ==24604== Memcheck, a memory error detector for x86-linux. ==24604== Copyright (C) 2002-2004, and GNU GPL'd, by Julian Seward et al. ==24604== Using valgrind-2.2.0, a program supervision framework for x86-linux. ==24604== Copyright (C) 2000-2004, and GNU GPL'd, by Julian Seward et al. ==24604== For more details, rerun with: -v ==24604== ==24604== Thread 3: ==24604== Invalid read of size 4 ==24604== at 0x3FC910: __nscd_get_map_ref (in /lib/tls/libc-2.3.4.so) ==24604== by 0x3FADB3: nscd_getpw_r (in /lib/tls/libc-2.3.4.so) ==24604== by 0x3FB106: __nscd_getpwuid_r (in /lib/tls/libc-2.3.4.so) ==24604== by 0x38E269: getpwuid_r@@GLIBC_2.1.2 (in /lib/tls/libc-2.3.4.so) ==24604== Address 0xB is not stack'd, malloc'd or (recently) free'd ==24604== ==24604== Process terminating with default action of signal 11 (SIGSEGV) ==24604== Access not within mapped region at address 0xB ==24604== at 0x3FC910: __nscd_get_map_ref (in /lib/tls/libc-2.3.4.so) ==24604== by 0x3FADB3: nscd_getpw_r (in /lib/tls/libc-2.3.4.so) ==24604== by 0x3FB106: __nscd_getpwuid_r (in /lib/tls/libc-2.3.4.so) ==24604== by 0x38E269: getpwuid_r@@GLIBC_2.1.2 (in /lib/tls/libc-2.3.4.so) ==24604== ==24604== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 15 from 1) ==24604== malloc/free: in use at exit: 29568 bytes in 23 blocks. ==24604== malloc/free: 33 allocs, 10 frees, 31088 bytes allocated. ==24604== For a detailed leak analysis, rerun with: --leak-check=yes ==24604== For counts of detected errors, rerun with: -v
The testcase provided in #2 has 2 fatal bugs in it: 1) pthread_create is called with &nIterations as last argument (i.e. address of an integer variable), but in the thread function it is used as if it was an integer variable: nIterations = (int) pArg. Either pthread_create should be called with (void *) (long) nIterations as last argument and thread function can stay as is, or main can stay as is and thread function needs to do nIterations = *(int *) pArg; (that's the variant I have tested). If &nIterations is in the upper half of 32-bit virtual address space (that will be usually the case, as stack on i?86 is usually 0xbf......) or if on 64-bit arches (int) &nIterations is negative or zero, getpwuid_r is not called at all in the thread and therefore the subsequent printf segfaults 2) _SC_GETPW_R_SIZE_MAX is just a magic cookie, not maximum size. You want sysconf (_SC_GETPW_R_SIZE_MAX) instead. If I correct these bugs, I couldn't reproduce any crashes.
Created attachment 119984 [details] Cleaned getpwuid test program. Build with.... cc -Wall -o getpwuid_r_rc getpwuid_r_rc.c cc -Wall -pthread -o getpwuid_r_rc_mt -DWITH_THREADS getpwuid_r_rc.c
And where does it say that not finding the requested entry is necessarily and error? If there will be say disk read error while trying to find the requested entry, an error obviously occurred. But merely not finding it because it isn't there isn't necessarily an error. Do you have other explanation why: "A NULL pointer shall be returned at the location pointed to by result on error or if the requested entry is not found." mentions explicitly if the requested entry is not found? If that would be always considered an error, it would be enough to say "A NULL pointer shall be returned at the location pointed to by result on error." BTW, if you look at man getpwuid_r: ERRORS 0 or ENOENT or ESRCH or EBADF or EPERM or ... The given name or uid was not found.
The POSIX spec is very clear (you can also read the 3p man page for getpwuid_r): ==== A null pointer shall be returned if the requested entry is not found, or an error occurs. On error, errno shall be set to indicate the error. ==== And then ===== If successful, the getpwuid_r() function shall return zero; otherwise, an error number shall be returned to indicate the error. ====== So, it's clear that the correct return value in case no entry is found is zero. Not finding a result is no error.