Bug 426751 - Valgrind reports «still reachable» memory using musl (alpine running inside docker)
Summary: Valgrind reports «still reachable» memory using musl (alpine running inside d...
Status: RESOLVED FIXED
Alias: None
Product: valgrind
Classification: Developer tools
Component: memcheck (show other bugs)
Version: 3.15 SVN
Platform: unspecified Linux
: NOR normal
Target Milestone: ---
Assignee: Paul Floyd
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-09-19 22:42 UTC by Marco
Modified: 2023-08-22 19:23 UTC (History)
3 users (show)

See Also:
Latest Commit:
Version Fixed In:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Marco 2020-09-19 22:42:40 UTC
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
Comment 1 ledrageur 2022-11-11 14:35:37 UTC
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)
Comment 2 Paul Floyd 2022-11-14 07:31:38 UTC
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.
Comment 3 Paul Floyd 2023-01-26 13:51:07 UTC
Never did get round to checking. I'm not sure that it is libc allocating the memory - could be libstc++ as well.
Comment 4 Paul Floyd 2023-08-20 13:30:31 UTC
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.
Comment 5 Paul Floyd 2023-08-20 13:34:24 UTC
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.