Valgrind fails to detect musl allocations using Alpine under Docker. A simple hello world program reports 468 bytes as still reachable. STEPS TO REPRODUCE 1. Download an install docker, and docker-compose. 2. Create a folder with a file named Dockerfile and one named docker-compose.yml Dockerfile contents: ``` FROM alpine RUN apk update && \ apk add vim bash nasm gcc clang gdb valgrind make mandoc man-pages less libc-dev && \ rm -rf /var/cache/apk/* RUN mkdir /code WORKDIR /code CMD ["/bin/bash"] ``` docker-compose.yml contens: ``` version: '3' services: test-musl: build: . volumes: - ./:/code ``` 3. Inside the newly created folder, run: docker-compose build test-musl docker-compose run test-musl bash 4. Inside docker, create a file named main.c main.c contents: ```c #include <stdio.h> int main(void) { printf("Hello, World\n"); return 0; } ``` 5. make main 6. valgrind --leak-check=full --show-leak-kinds=all ./main OBSERVED RESULT ``` bash-5.0# valgrind --leak-check=full --show-leak-kinds=all ./main ==19== Memcheck, a memory error detector ==19== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==19== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info ==19== Command: ./main ==19== Hola, Mundo! ==19== ==19== HEAP SUMMARY: ==19== in use at exit: 468 bytes in 4 blocks ==19== total heap usage: 5 allocs, 1 frees, 508 bytes allocated ==19== ==19== 16 bytes in 2 blocks are still reachable in loss record 1 of 3 ==19== at 0x48A16F2: calloc (vg_replace_malloc.c:762) ==19== by 0x40590F4: ??? (in /lib/ld-musl-x86_64.so.1) ==19== ==19== 32 bytes in 1 blocks are still reachable in loss record 2 of 3 ==19== at 0x48A16F2: calloc (vg_replace_malloc.c:762) ==19== by 0x40590F4: ??? (in /lib/ld-musl-x86_64.so.1) ==19== by 0x57ACD5F4220F7227: ??? ==19== by 0x2D616C6F682F2ED2: ??? ==19== by 0x3878006F646E756C: ??? ==19== ==19== 420 bytes in 1 blocks are still reachable in loss record 3 of 3 ==19== at 0x48A16F2: calloc (vg_replace_malloc.c:762) ==19== by 0x4058EBC: ??? (in /lib/ld-musl-x86_64.so.1) ==19== by 0x4059B14: __dls3 (in /lib/ld-musl-x86_64.so.1) ==19== by 0x57ACD5F4220F7227: ??? ==19== by 0x2D616C6F682F2ED2: ??? ==19== by 0x3878006F646E756C: ??? ==19== ==19== LEAK SUMMARY: ==19== definitely lost: 0 bytes in 0 blocks ==19== indirectly lost: 0 bytes in 0 blocks ==19== possibly lost: 0 bytes in 0 blocks ==19== still reachable: 468 bytes in 4 blocks ==19== suppressed: 0 bytes in 0 blocks ==19== ==19== For lists of detected and suppressed errors, rerun with: -s ==19== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) ``` EXPECTED RESULT Nothing showing up in the "still reachable" part. SOFTWARE/OS VERSIONS Linux: Ubuntu 20.04 LTS ADDITIONAL INFORMATION This bug seems similar to this other one: https://bugs.kde.org/show_bug.cgi?id=408663 Except that the suppression file posted there doesn't quite work for this use case. Generated the suppressions file: ``` { "Sup 3 test" Memcheck:Leak match-leak-kinds: reachable fun:calloc obj:/lib/ld-musl-x86_64.so.1 fun:__dls3 obj:* obj:* obj:* } { "Sup 2 test" Memcheck:Leak match-leak-kinds: reachable fun:calloc obj:/lib/ld-musl-x86_64.so.1 obj:* obj:* obj:* } { "Sup 1 test" Memcheck:Leak match-leak-kinds: reachable fun:calloc obj:/lib/ld-musl-x86_64.so.1 } ``` A similar bug reported in stackoverflow: https://stackoverflow.com/questions/61774643/valgrind-on-alpine-linux
Did someone make something for this bug? Because today I reproduce it with a very very simple exemple : #include <iostream> int main() { return 0; } It got me this result : ==15== Memcheck, a memory error detector ==15== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==15== Using Valgrind-3.19.0-8d3c8034b8-20220411 and LibVEX; rerun with -h for copyright info ==15== Command: ./toto ==15== Parent PID: 1 ==15== --15-- --15-- Valgrind options: --15-- --track-origins=yes --15-- --keep-debuginfo=yes --15-- --read-inline-info=yes --15-- --error-exitcode=1 --15-- --leak-check=full --15-- --errors-for-leak-kinds=all --15-- --show-leak-kinds=all --15-- --verbose --15-- --sigill-diagnostics=no --15-- --log-file=valgrind-out.txt --15-- Contents of /proc/version: --15-- Linux version 5.10.16.3-microsoft-standard-WSL2 (oe-user@oe-host) (x86_64-msft-linux-gcc (GCC) 9.3.0, GNU ld (GNU Binutils) 2.34.0.20200220) #1 SMP Fri Apr 2 22:23:49 UTC 2021 --15-- --15-- Arch and hwcaps: AMD64, LittleEndian, amd64-cx16-lzcnt-rdtscp-sse3-ssse3-avx-avx2-bmi-f16c-rdrand-rdseed --15-- Page sizes: currently 4096, max supported 4096 --15-- Valgrind library directory: /usr/libexec/valgrind --15-- Reading syms from /src/toto --15-- Reading syms from /lib/ld-musl-x86_64.so.1 --15-- object doesn't have a symbol table --15-- Reading syms from /usr/libexec/valgrind/memcheck-amd64-linux --15-- object doesn't have a dynamic symbol table --15-- Scheduler: using generic scheduler lock implementation. --15-- Reading suppressions file: /usr/libexec/valgrind/default.supp ==15== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-15-by-???-on-a0139202b827 ==15== embedded gdbserver: writing to /tmp/vgdb-pipe-to-vgdb-from-15-by-???-on-a0139202b827 ==15== embedded gdbserver: shared mem /tmp/vgdb-pipe-shared-mem-vgdb-15-by-???-on-a0139202b827 ==15== ==15== TO CONTROL THIS PROCESS USING vgdb (which you probably ==15== don't want to do, unless you know exactly what you're doing, ==15== or are doing some strange experiment): ==15== /usr/libexec/valgrind/../../bin/vgdb --pid=15 ...command... ==15== ==15== TO DEBUG THIS PROCESS USING GDB: start GDB like this ==15== /path/to/gdb ./toto ==15== and then give GDB the following command ==15== target remote | /usr/libexec/valgrind/../../bin/vgdb --pid=15 ==15== --pid is optional if only one valgrind process is running ==15== --15-- Reading syms from /usr/libexec/valgrind/vgpreload_core-amd64-linux.so --15-- object doesn't have a symbol table --15-- Reading syms from /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so --15-- object doesn't have a symbol table --15-- REDIR: 0x405082f (libc.musl-x86_64.so.1:strlen) redirected to 0x48a7b10 (strlen) --15-- REDIR: 0x40505f1 (libc.musl-x86_64.so.1:strcpy) redirected to 0x48a7b72 (strcpy) --15-- REDIR: 0x4050534 (libc.musl-x86_64.so.1:strchr) redirected to 0x48a787c (strchr) --15-- REDIR: 0x4050935 (libc.musl-x86_64.so.1:strncmp) redirected to 0x48a7f6a (strncmp) --15-- REDIR: 0x40509bd (libc.musl-x86_64.so.1:strnlen) redirected to 0x48a7aea (strnlen) --15-- REDIR: 0x4050a80 (libc.musl-x86_64.so.1:strspn) redirected to 0x48ab61a (strspn) --15-- REDIR: 0x4050601 (libc.musl-x86_64.so.1:strcspn) redirected to 0x48ab5ba (strcspn) --15-- Reading syms from /usr/lib/libstdc++.so.6.0.29 --15-- object doesn't have a symbol table --15-- REDIR: 0x40509f9 (libc.musl-x86_64.so.1:strrchr) redirected to 0x48a77e9 (strrchr) --15-- Reading syms from /usr/lib/libgcc_s.so.1 --15-- object doesn't have a symbol table --15-- REDIR: 0x4023529 (libc.musl-x86_64.so.1:malloc) redirected to 0x48a2650 (malloc) --15-- REDIR: 0x40505d9 (libc.musl-x86_64.so.1:strcmp) redirected to 0x48a847c (strcmp) ==15== ==15== HEAP SUMMARY: ==15== in use at exit: 72,704 bytes in 1 blocks ==15== total heap usage: 1 allocs, 0 frees, 72,704 bytes allocated ==15== ==15== Searching for pointers to 1 not-freed blocks ==15== Checked 51,200 bytes ==15== ==15== 72,704 bytes in 1 blocks are still reachable in loss record 1 of 1 ==15== at 0x48A26D5: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==15== by 0x4974557: ??? (in /usr/lib/libstdc++.so.6.0.29) ==15== by 0x4059482: ??? (in /lib/ld-musl-x86_64.so.1) ==15== by 0x409693F: ??? ==15== by 0x4A7651F: ??? (in /usr/lib/libstdc++.so.6.0.29) ==15== by 0x1FFF000CFF: ??? ==15== by 0xB907FED: ??? ==15== by 0x5C1A2: ??? ==15== by 0xCE9F: ??? ==15== by 0x1CEAEF: ??? ==15== ==15== LEAK SUMMARY: ==15== definitely lost: 0 bytes in 0 blocks ==15== indirectly lost: 0 bytes in 0 blocks ==15== possibly lost: 0 bytes in 0 blocks ==15== still reachable: 72,704 bytes in 1 blocks ==15== suppressed: 0 bytes in 0 blocks ==15== ==15== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) Note that if I delete the "#include <iostream>" : there is no errors. For total reproduction, I use this dockerfile: FROM alpine RUN apk update && apk add libstdc++ g++ make linux-headers valgrind WORKDIR /src CMD ["make"] and this makefile : MAKEFLAGS += --no-print-directory CC=g++ CFLAGS=-W -Wall -Wunused -Wshadow -Wpointer-arith -Wcast-qual -Wno-missing-braces -ansi -pedantic -std=c++20 -I/usr/local/include LDFLAGS=-L /usr/lib/openssl/lib -L /usr/lib EXEC_DEBUG=toto DEBOBJDIR=./obj/debug SRCDIR=. SRC_FILES=$(wildcard $(SRCDIR)/*/*/*.cpp $(SRCDIR)/*/*.cpp $(SRCDIR)/*.cpp) DEBOBJ= $(SRC_FILES:%.cpp=$(DEBOBJDIR)/%.o) DEPS= $(DEBOBJ:.o=.d) MEMORYCHECKER=valgrind MEMORY_RESULT_FILE=valgrind-out.txt VALGRIND_FLAGS=--error-exitcode=1 --leak-check=full --errors-for-leak-kinds=all --show-leak-kinds=all --verbose --sigill-diagnostics=no --log-file=$(MEMORY_RESULT_FILE) VALGRIND_FULL_FLAGS=--track-origins=yes --keep-debuginfo=yes --read-inline-info=yes .PHONY: all clear all: $(EXEC_DEBUG) fullMemoryCheck -include $(DEPS) $(EXEC_DEBUG): $(DEBOBJ) $(CC) -o $@ $^ $(LDFLAGS) $(DEBOBJDIR)/%.o : %.cpp makefile @mkdir -p $(dir $@) $(CC) -o $@ -MP -MMD -c $< $(CFLAGS) -g clean: find $(DEBOBJDIR) -name "*.o" -type f -delete find $(DEBOBJDIR) -name "*.d" -type f -delete clear: clean rm -rf $(EXEC) $(EXEC_DEBUG) fullMemoryCheck: $(EXEC_DEBUG) @export GLIBCXX_FORCE_NEW $(MEMORYCHECKER) $(VALGRIND_FULL_FLAGS) $(VALGRIND_FLAGS) ./$(EXEC_DEBUG)
In short, no. I occasionally fire up an Alpine VM to check the code builds. I'm not aware of any other devs using musl at all. As a result the state of Valgrind with musl is fairly dire (though not as bad as macOS). There are a couple of hundred regtests that don't run due to missing dependencies. And over a hundred regtest failures. A full fix of this would require musl libc to have a __libc_freeres function for Valgrind to hook into and call at exit() and thereby free all one-off allocations for stuff like IO caches. That would require quite a lot of work on the musl libc side. Otherwise there is always the suppression file. I might get round to looking into that this week as I've been doing some testing on musl for another bugzilla item.
Never did get round to checking. I'm not sure that it is libc allocating the memory - could be libstc++ as well.
hello world in C++ for me has 8 still reachable 6 small ones in glib 1 16k in glib 1 72k in libstdc++ glib is rather stinky when it comes to cleaning up memory the libstdc++ memory should be freed via the --run-cxx-freeres=yes mechanism I just pushed this commit 0c37fa39c104f9c7c5f0b2297e3d52e8b8d58cac (HEAD -> master, origin/master, origin/HEAD) Author: Paul Floyd <pjfloyd@wanadoo.fr> Date: Sun Aug 20 15:24:25 2023 +0200 musl: enable libstdc++ freeres Both libc and libstdc++ freeres were disabled for musl. That means that libstdc++ was showing still reachable memory on systems like Alpine. that should help for C++ applications.
I think the glib memory was due to some other library (I was also trying to debug https://bugs.kde.org/show_bug.cgi?id=472409) Will probably close this soon.