NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

lib/59599: libc looses environment updates made during .so initialization



>Number:         59599
>Category:       lib
>Synopsis:       libc looses environment updates made during .so initialization
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Aug 15 23:20:00 +0000 2025
>Originator:     Aliaksei Kandratsenka
>Release:        10.1
>Organization:
>Environment:
NetBSD netbsd10 10.1 NetBSD 10.1 (GENERIC) #0: Mon Dec 16 13:08:11 UTC 2024  mkrepro%mkrepro.NetBSD.org@localhost:/usr/src/sys/arch/amd64/compile/GENERIC amd64

>Description:
I am making a fix to gperftools for how it handles asking child processes to use distinct cpu profile files. Issue described here: https://github.com/gperftools/gperftools/issues/1603

The fix involves updating environ variable. And things work (on GNU/Linux and FreeBSD, at least). Except on NetBSD tests failed. Debugging this I found that some dynamic linker facility is actually overwriting environ variable (presumably back to what the kernel set) after shared library initializers have run.

Here is the disasm of the place that overwrites environ that I caught with some obvious gdb-ing:

Hardware watchpoint 4: *$29

Old value = (char **) 0x7cd789d09000
New value = (char **) 0x7f7fff51dca8
___start (cleanup=0x7f7f6f0067f6 <_rtld_exit>, ps_strings=0x7f7fff51efe0) at /usr/src/lib/csu/common/crt0-common.c:310
310	/usr/src/lib/csu/common/crt0-common.c: No such file or directory.
(gdb) bt
#0  ___start (cleanup=0x7f7f6f0067f6 <_rtld_exit>, ps_strings=0x7f7fff51efe0) at /usr/src/lib/csu/common/crt0-common.c:310
#1  0x00007f7f6f00baf8 in ?? () from /usr/libexec/ld.elf_so
#2  0x0000000000000003 in ?? ()
#3  0x00007f7fff51e258 in ?? ()
#4  0x00007f7fff51e28b in ?? ()
#5  0x00007f7fff51e28d in ?? ()
#6  0x0000000000000000 in ?? ()
(gdb) disas
Dump of assembler code for function ___start:
   0x0000000000400ed1 <+0>:	push   %rbp
   0x0000000000400ed2 <+1>:	mov    %rsp,%rbp
   0x0000000000400ed5 <+4>:	push   %r14
   0x0000000000400ed7 <+6>:	push   %r13
   0x0000000000400ed9 <+8>:	push   %r12
   0x0000000000400edb <+10>:	push   %rbx
   0x0000000000400edc <+11>:	mov    %rdi,%r14
   0x0000000000400edf <+14>:	mov    %rsi,%r12
   0x0000000000400ee2 <+17>:	mov    0x18(%rsi),%edx
   0x0000000000400ee5 <+20>:	mov    0x8(%rsi),%esi
   0x0000000000400ee8 <+23>:	mov    (%r12),%rdi
   0x0000000000400eec <+27>:	call   0x400d82 <relocate_self>
   0x0000000000400ef1 <+32>:	mov    $0x602648,%rax
   0x0000000000400ef8 <+39>:	mov    %r12,(%rax)
   0x0000000000400efb <+42>:	mov    $0x602680,%r13
   0x0000000000400f02 <+49>:	mov    0x10(%r12),%rax
   0x0000000000400f07 <+54>:	mov    %rax,0x0(%r13)
=> 0x0000000000400f0b <+58>:	mov    (%r12),%rax
   0x0000000000400f0f <+62>:	mov    (%rax),%rdx
   0x0000000000400f12 <+65>:	test   %rdx,%rdx
   0x0000000000400f15 <+68>:	je     0x400fd4 <___start+259>
   0x0000000000400f1b <+74>:	mov    $0x602340,%rcx
   0x0000000000400f22 <+81>:	mov    %rdx,(%rcx)
   0x0000000000400f25 <+84>:	mov    (%rax),%rax
   0x0000000000400f28 <+87>:	movzbl (%rax),%edx
   0x0000000000400f2b <+90>:	test   %dl,%dl
   0x0000000000400f2d <+92>:	je     0x400f42 <___start+113>

>How-To-Repeat:
I prepared simple reproduction:

--- test-environ-so.c ---
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern char** environ;

#define CHECK(cond) do { if (!(cond)) { __builtin_trap(); } } while (0)

void verify_foobar();

static __attribute__((constructor))
void test_constructor() {
	// first, ensure FOOBAR env. variable is missing
	CHECK(getenv("FOOBAR") == NULL);
	// now add it
	/*
	 * setenv("FOOBAR", "1", 1);
	 */

	static char* new_env[] = {"FOOBAR=1", NULL};
	environ = new_env;

	verify_foobar();

	printf("FOOBAR setup done\n");
}

void verify_foobar() {
	char* fb = getenv("FOOBAR");
	CHECK(fb != NULL);
	CHECK(strcmp(fb, "1") == 0);
}
---

--- test-environ-main.c ---
#include <stdio.h>

extern void verify_foobar();

int main() {
	printf("main started\n");
	verify_foobar();
	printf("foobar verified\n");
	return 0;
}
---

Then build .so and main for example like this:

netbsd10# gcc -O2 -ggdb3 -fPIC -shared -Wl,-soname,libtest-environ.so.0  -o libtest-environ.so.0.0.1 test-environ-so.c
netbsd10# ln -s libtest-environ.so.0.0.1 libtest-environ.so.0
netbsd10# gcc -O2 -ggdb3 -o test-environ test-environ-main.c -Wl,-rpath,. libtest-environ.so.0.0.1
netbsd10# ldd ./test-environ
./test-environ:
	-ltest-environ.0 => ./libtest-environ.so.0
	-lc.12 => /usr/lib/libc.so.12
netbsd10# ./test-environ
FOOBAR setup done
main started
[1]   Illegal instruction (core dumped) ./test-environ

>Fix:



Home | Main Index | Thread Index | Old Index