| Issue |
181389
|
| Summary |
Sanitizer interception crashes NSS functions on systems with NIS (RHEL 10+, Debian 12+)
|
| Labels |
new issue
|
| Assignees |
|
| Reporter |
kirelagin
|
This is basically a special case of #181388 with an additional complication of #133338. I thought it deserved a separate issue, as it might become a bigger problem as more people upgrade to Debian 12 / RHEL 10.
The code in `example.c` is the example from the bottom of `man getpwnam_r`.
<details><summary>example.c</summary>
<p>
```c
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int
main(int argc, char *argv[])
{
struct passwd pwd;
struct passwd *result;
char *buf;
size_t bufsize;
int s;
if (argc != 2) {
fprintf(stderr, "Usage: %s username\n", argv[0]);
exit(EXIT_FAILURE);
}
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (bufsize == -1) /* Value was indeterminate */
bufsize = 16384; /* Should be more than enough */
buf = malloc(bufsize);
if (buf == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
s = getpwnam_r(argv[1], &pwd, buf, bufsize, &result);
if (result == NULL) {
if (s == 0)
printf("Not found\n");
else {
errno = s;
perror("getpwnam_r");
}
exit(EXIT_FAILURE);
}
printf("Name: %s; UID: %ld\n", pwd.pw_gecos, (long) pwd.pw_uid);
exit(EXIT_SUCCESS);
}
```
</p>
</details>
```shell_session
$ clang example.c -o example
$ ./example bad-user
Not found
$ clang -fsanitize=address example.c -o example
$ ./example bad-user
AddressSanitizer:DEADLYSIGNAL
=================================================================
==3676120==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000000000000 bp 0x7fffffffa8e0 sp 0x7fffffffa0a8 T0)
==3676120==Hint: pc points to the zero page.
==3676120==The signal is caused by a READ memory access.
==3676120==Hint: address points to the zero page.
#0 0x000000000000 (<unknown module>)
#1 0x7ffff78905c1 (/nix/store/<hash>-libnsl-2.0.1/lib/libnsl.so.3+0x35c1)
#2 0x7ffff7890a5d (/nix/store/<hash>-libnsl-2.0.1/lib/libnsl.so.3+0x3a5d)
==3676120==Register values:
rax = 0x0000000000000001 rbx = 0x00007fffffffa970 rcx = 0x0000000000000000 rdx = 0x0000000000000001
rdi = 0x00007fffffffa970 rsi = 0x0000515000000a80 rbp = 0x00007fffffffa8e0 rsp = 0x00007fffffffa0a8
r8 = 0x0000000000000000 r9 = 0x0000000000000000 r10 = 0x0000000000000000 r11 = 0x0000000000000000
r12 = 0x00007fffffffaa30 r13 = 0x0000515000000a80 r14 = 0x00005120000004c0 r15 = 0x0000000000000001
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (<unknown module>)
==3676120==ABORTING
```
This is Clang 19.1.6 (but, I believe, `main` is affected too) running on a RHEL 8 machine (but with glibc 2.40), with NSS configured to use NIS for passwd (`libnss_nis`).
The sequence of steps leading to the issue is essentially the same as in #181388:
1. The executable starts, `libclang_rt.asan` resolves the real functions for its interceptors.
2. The `xdrstdio_create` function has been deprecated, so `dlsym` returns nullptr.
3. When trying to locate the user, NSS loads `libnss_nis.so` as a plugin.
4. `libnss_nis.so` depends on `libnsl`, which depends on `libtirpc`, which is where the deprecated function was moved to.
5. `libnsl.so` calls `xdrstdio_create`, `___interceptor_xdrstdio_create` tries to call nullptr.
<details><summary>Full backtrace</summary>
<p>
```text
#0 0x0000000000000000 in ?? ()
#1 0x00005555555b3bf9 in ___interceptor_xdrstdio_create.part.0 ()
#2 0x00007ffff78905c2 in __yp_bind.part.0 () from /nix/store/<hash>-libnsl-2.0.1/lib/libnsl.so.3
#3 0x00007ffff7890a5e in do_ypcall () from /nix/store/<hash>-libnsl-2.0.1/lib/libnsl.so.3
#4 0x00007ffff7890c69 in do_ypcall_tr () from /nix/store/<hash>-libnsl-2.0.1/lib/libnsl.so.3
#5 0x00007ffff7891605 in yp_match () from /nix/store/<hash>-libnsl-2.0.1/lib/libnsl.so.3
#6 0x00007ffff789ed57 in _nss_nis_getpwnam_r () from /nix/store/<hash>-nss-libs/lib/libnss_nis.so.2
#7 0x00007ffff7ddbd23 in __getpwnam_r (name=<optimized out>, resbuf=0x7ffff5e00020, buffer=<optimized out>, buflen=1024, result=<optimized out>) at ../nss/getXXbyYY_r.c:273
#8 0x0000555555645f37 in __interceptor_getpwnam_r ()
#9 0x00005555556bbed6 in main ()
```
</p>
</details>
This only crashes with glibc >= 2.36. In earlier versions, the crash was hidden by a glibc bug (see #133338 for details).
So, on RHEL 8 and 9, the “real” function is incorrectly resolved to the deprecated symbol in glibc, instead of the actual implementation in the library that it has been moved to, which is, arguably, even worse than a crash.
I do not have a RHEL 10 machine to test on, but, I assume, the code would crash on RHEL 10 with NIS configured, so it is only a matter of time until enterprise users, who rely on NIS, migrate to RHEL 10 and begin observing the problem. I believe, the situation with Debian 12 is the same.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs