Bug 32498 - Leaks from call_init (dl-init.c:74)
Summary: Leaks from call_init (dl-init.c:74)
Status: RESOLVED MOVED
Alias: None
Product: glibc
Classification: Unclassified
Component: dynamic-link (show other bugs)
Version: 2.40
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-12-24 09:13 UTC by Paul Floyd
Modified: 2024-12-26 09:19 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed: 2024-12-25 00:00:00
Project(s) to access:
ssh public key:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Paul Floyd 2024-12-24 09:13:46 UTC
This is related to the following Valgrind bugzilla item https://bugs.kde.org/show_bug.cgi?id=358980 from comment 3 onwards.

The leaks that I get are

==16702== HEAP SUMMARY:
==16702==     in use at exit: 24,879 bytes in 29 blocks
==16702==   total heap usage: 335 allocs, 306 frees, 233,296 bytes allocated
==16702== 
==16702== 8 bytes in 1 blocks are definitely lost in loss record 1 of 15
==16702==    at 0x48447C2: malloc (vg_replace_malloc.c:446)
==16702==    by 0x96A0A35: ???
==16702==    by 0x96B788F: ???
==16702==    by 0x969F1BB: ???
==16702==    by 0x40045B6: call_init (dl-init.c:74)
==16702==    by 0x40045B6: call_init (dl-init.c:26)
==16702==    by 0x40046AC: _dl_init (dl-init.c:121)
==16702==    by 0x40015C1: _dl_catch_exception (dl-catch.c:215)
==16702==    by 0x400B4FB: dl_open_worker (dl-open.c:829)
==16702==    by 0x4001522: _dl_catch_exception (dl-catch.c:241)
==16702==    by 0x400B903: _dl_open (dl-open.c:905)
==16702==    by 0x4CA3F13: dlopen_doit (dlopen.c:56)
==16702==    by 0x4001522: _dl_catch_exception (dl-catch.c:241)
==16702==    by 0x4001678: _dl_catch_error (dl-catch.c:260)
==16702==    by 0x4CA39F2: _dlerror_run (dlerror.c:138)
==16702==    by 0x4CA3FCE: dlopen_implementation (dlopen.c:71)
==16702==    by 0x4CA3FCE: dlopen@@GLIBC_2.34 (dlopen.c:81)
==16702==    by 0x10917D: main (main.cpp:6)
==16702== 
==16702== 96 bytes in 1 blocks are definitely lost in loss record 10 of 15
==16702==    at 0x484BEFF: calloc (vg_replace_malloc.c:1675)
==16702==    by 0x96A0A6D: ???
==16702==    by 0x969F100: ???
==16702==    by 0x969F1D7: ???
==16702==    by 0x40045B6: call_init (dl-init.c:74)
==16702==    by 0x40045B6: call_init (dl-init.c:26)
==16702==    by 0x40046AC: _dl_init (dl-init.c:121)
==16702==    by 0x40015C1: _dl_catch_exception (dl-catch.c:215)
==16702==    by 0x400B4FB: dl_open_worker (dl-open.c:829)
==16702==    by 0x4001522: _dl_catch_exception (dl-catch.c:241)
==16702==    by 0x400B903: _dl_open (dl-open.c:905)
==16702==    by 0x4CA3F13: dlopen_doit (dlopen.c:56)
==16702==    by 0x4001522: _dl_catch_exception (dl-catch.c:241)
==16702==    by 0x4001678: _dl_catch_error (dl-catch.c:260)
==16702==    by 0x4CA39F2: _dlerror_run (dlerror.c:138)
==16702==    by 0x4CA3FCE: dlopen_implementation (dlopen.c:71)
==16702==    by 0x4CA3FCE: dlopen@@GLIBC_2.34 (dlopen.c:81)
==16702==    by 0x10917D: main (main.cpp:6)
==16702== 
==16702== LEAK SUMMARY:
==16702==    definitely lost: 104 bytes in 2 blocks
==16702==    indirectly lost: 0 bytes in 0 blocks
==16702==      possibly lost: 0 bytes in 0 blocks
==16702==    still reachable: 24,775 bytes in 27 blocks
==16702==         suppressed: 0 bytes in 0 blocks
==16702== Reachable blocks (those to which a pointer was found) are not shown.
==16702== To see them, rerun with: --leak-check=full --show-leak-kinds=all


I'm ignoring the still-reachables for now.

Details:
Arch linux updated on 24 Dec 2024 running in a VirtualBox VM
[paulf@archlinux dlopen-leaks]$ lscpu
Architecture:             x86_64
  CPU op-mode(s):         32-bit, 64-bit
  Address sizes:          36 bits physical, 48 bits virtual
  Byte Order:             Little Endian
CPU(s):                   1
  On-line CPU(s) list:    0
Vendor ID:                GenuineIntel
  Model name:             Intel(R) Xeon(R) CPU           W3520  @ 2.67GHz
    CPU family:           6
    Model:                26
    Thread(s) per core:   1
    Core(s) per socket:   1
    Socket(s):            1
    Stepping:             5
    BogoMIPS:             5335.55
    Flags:                fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopolo
                          gy nonstop_tsc cpuid tsc_known_freq pni monitor ssse3 cx16 sse4_1 sse4_2 x2apic popcnt hypervisor lahf_lm pti


Valgrind 3.24.0 (same with Valgrind built from git HEAD).

gcc (GCC) 14.2.1 20240910

[paulf@archlinux dlopen-leaks]$ /lib/ld-linux-x86-64.so.2 --version
ld.so (GNU libc) stable release version 2.40.
Comment 1 Florian Weimer 2024-12-25 08:43:39 UTC
Please add a reproducer. The valgrind output is consistent with loading a shared object with dlopen, and the shared object has an ELF constructor that calls malloc (perhaps indirectly).
Comment 2 Paul Floyd 2024-12-25 11:19:50 UTC
From the Valgrind item

git clone https://github.com/leshe4ka46/dlopen-leaks
cd dlopen-leaks
mkdir build
cmake ..
make
cd ..
./build/dlopen_test

There are quite a few dependencies, particularly Qt6.

I don't get a leak on Fedora 41, also glibc 2.40.

I'll try to see if I can get any more info about the callstack entries that seem to be stripped.
Comment 3 Florian Weimer 2024-12-25 11:44:25 UTC
(In reply to Paul Floyd from comment #2)
> From the Valgrind item
> 
> git clone https://github.com/leshe4ka46/dlopen-leaks
> cd dlopen-leaks
> mkdir build
> cmake ..
> make
> cd ..
> ./build/dlopen_test
> 
> There are quite a few dependencies, particularly Qt6.
> 
> I don't get a leak on Fedora 41, also glibc 2.40.
> 
> I'll try to see if I can get any more info about the callstack entries that
> seem to be stripped.

The reproducer does not run under valgrind because it conflicts with Address Sanitizer.

If you can reproduce it on Fedora 41, please run the reproducer under valgrind with:

DEBUGINFOD_URLS=https://debuginfod.fedoraproject.org/ valgrind build/dlopen_test
Comment 4 Paul Floyd 2024-12-25 19:14:03 UTC
I forgot, you need to comment out asan:

diff --git a/CMakeLists.txt b/CMakeLists.txt
index be1ab50..799b278 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,7 +18,7 @@ endif()
 project(dlopen_test CXX)
 
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -g -O0")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,undefined")
+#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,undefined")

I don't get the same problem with Fedora.v

Better results wiith "valgrind --leak-check=full --keep-debuginfo=yes ./build/dlopen_test"

==6339== 8 bytes in 1 blocks are definitely lost in loss record 1 of 15
==6339==    at 0x48447A8: malloc (vg_replace_malloc.c:446)
==6339==    by 0x96A0A35: gomp_malloc (alloc.c:38)
==6339==    by 0x96B788F: gomp_init_num_threads (proc.c:91)
==6339==    by 0x969F1BB: initialize_env (env.c:2218)
==6339==    by 0x40045B6: call_init (dl-init.c:74)
==6339==    by 0x40045B6: call_init (dl-init.c:26)
==6339==    by 0x40046AC: _dl_init (dl-init.c:121)
==6339==    by 0x40015C1: _dl_catch_exception (dl-catch.c:215)
==6339==    by 0x400B4FB: dl_open_worker (dl-open.c:829)
==6339==    by 0x4001522: _dl_catch_exception (dl-catch.c:241)
==6339==    by 0x400B903: _dl_open (dl-open.c:905)
==6339==    by 0x4CA3F13: dlopen_doit (dlopen.c:56)
==6339==    by 0x4001522: _dl_catch_exception (dl-catch.c:241)
==6339== 
==6339== 96 bytes in 1 blocks are definitely lost in loss record 10 of 15
==6339==    at 0x484BC13: calloc (vg_replace_malloc.c:1675)
==6339==    by 0x96A0A6D: gomp_malloc_cleared (alloc.c:47)
==6339==    by 0x969F100: add_initial_icv_to_list (env.c:2159)
==6339==    by 0x969F1D7: initialize_env (env.c:2224)
==6339==    by 0x40045B6: call_init (dl-init.c:74)
==6339==    by 0x40045B6: call_init (dl-init.c:26)
==6339==    by 0x40046AC: _dl_init (dl-init.c:121)
==6339==    by 0x40015C1: _dl_catch_exception (dl-catch.c:215)
==6339==    by 0x400B4FB: dl_open_worker (dl-open.c:829)
==6339==    by 0x4001522: _dl_catch_exception (dl-catch.c:241)
==6339==    by 0x400B903: _dl_open (dl-open.c:905)
==6339==    by 0x4CA3F13: dlopen_doit (dlopen.c:56)
==6339==    by 0x4001522: _dl_catch_exception (dl-catch.c:241)
Comment 5 Florian Weimer 2024-12-25 20:00:20 UTC
This is an allocation in libgomp for storing CPU affinity sets.

The difference seems to be that dlclose on Fedora does not unload libgomp.so.1: libQt6Core.so.6 is marked NODELETE because it has a STB_GNU_UNIQUE symbol, it depends on libgomp.so.1, so libgomp.so.1 will stay around after that dlclose, too.

NODELETE markings are logged by sufficiently new versions of glibc:

LD_DEBUG=all  build/dlopen_test </dev/null |& grep NODELETE

You could report this as a GCC bug because libgomp could have an ELF destructor that frees the CPU set, or maybe a valgrind suppression file. Or maybe this allocation can be removed altogether because using a single global object as a scratch space does not look very thread-safe.
Comment 6 Paul Floyd 2024-12-26 09:00:50 UTC
Thanks for the quick answers.

From what you say
readelf -h --dyn-syms /usr/lib/libQt6Core.so.6 | grep UNIQUE

should give something on Fedora. I get nothing on Arch.
Comment 7 Paul Floyd 2024-12-26 09:19:41 UTC
And indeed it does. 256 UNIQUE DEFAULT symbols.