C Valgrind/memcheck not working ?

Hello,

As I wanted to make C code on a FreeBSD development environment, I tried to run valgrind (tool memcheck) to detect memory leaks. However, some problems occurred :
  • valgrind does not detect leaks in my test code - but detects invalid writes ;
  • symbols are not shown.
My test code is the following :
C:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
  char *test = malloc(10);

  strcpy(test, "dddddd");
  printf("test %s\n", test);

  test = malloc(42);
  strcpy(test, "dddddddddddddddddddd");
  printf("test %s\n", test);
 
  free(test);

  *(char *)0xdeadbeef = 0;
  return 0;
}

Compile command :
cc main.c -O0 -g -o test

Valgrind command (and relevant output parts):
Code:
valgrind --tool=memcheck --leak-check=yes --leak-resolution=high --track-origins=yes --undef-value-errors=yes --show-leak-kinds=all --track-fds=yes --trace-children=no --vgdb=no --show-reachable=yes --verbose --error-exitcode=1 ./test
[...]
--81349-- Reading syms from /root/TEST/test2
--81349-- ELF section outside all mapped regions
[...]
--81349-- Reading syms from /lib/libc.so.7
--81349-- ELF section outside all mapped regions
[...]
==81349== Invalid write of size 1
==81349==    at 0x201376: ??? (in /root/TEST/test2)
==81349==    by 0x20110E: ??? (in /root/TEST/test2)
==81349==    by 0x4828FFF: ???
==81349==  Address 0xdeadbeef is not stack'd, malloc'd or (recently) free'd
[...]
==81349== HEAP SUMMARY:
==81349==     in use at exit: 0 bytes in 0 blocks
==81349==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==81349== 
==81349== All heap blocks were freed -- no leaks are possible
==81349== 
==81349== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Can someone help me to understand :
  • why valgrind does not even show symbol in my test executable (whereas compiled with -g) ?
  • Is valgrind able to detect memory leaks without knowledge of symbols ?
  • Is there some special things to do to use the debug files, especially /usr/lib/debug/lib/libc.so.7.debug ?

Version information :
  • FreeBSD-12
  • valgrind 3.10.1.20160113_7,1 from binary repository (same issue when rebuilding it from port devel/valgrind or devel/valgrind-devel)

I also have a look at clang analyzers (asan and ubsan works, I love it), but lsan (leakSanitizer) has not been ported to FreeBSD. Also in ports I tried ElectricFence, which seems broken (segfault). However, google-perftools with the heap profiler works (but not the leak profiler), but only with code calling tc_malloc explicitly (not my use case).

I looked at jemalloc with MALLOC_CONF=prof_leak:true :
$ MALLOC_CONF=prof_leak:true ./test
<jemalloc>: Invalid conf pair: prof_leak:true
...

It seems that jemalloc included in /usr/src has not been build with --enable-prof.
I also try mprof, it works. But copying all required libraries in the test directory would be annoying with my build environment.

My main question is : is someone succeed in using valgrind/memcheck on FreeBSD 12 ?

Thanks in advance !
 
Just re-read your post a little more closely.
I have found valgrind to be very unreliable when it comes to printing backtraces. It almost seems random as to whether you get a list of function names or a list of virtual addresses in hexadecimal. I've never got it to work on FreeBSD, however I have Fedora 29 on my laptop and valgrind backtraces work fine there.
There is no technical reason why a debugger should need DWARF debug symbols in order to detect memory leaks. The usual way to patch malloc and friends is by using the "LD_PRELOAD trick" - forcing the program to load a shared object with alternative implementations of malloc etc. Not only does this not require debug symbols... It doesn't even require C code. You can do it from the shell prompt. (Interestingly, it's possible to cheat at some online games using LD_PRELOAD - for example by replacing a real random number generator with a customised one!) I imagine valgrind uses this trick.
Years ago I used valgrind to debug some programs written in x86-64 assembly so that further corroborates my speculation that debug symbols are not needed.
I think valgrind is just buggy. I've not found much use for it on FreeBSD.
Wish I could help more. Maybe someone else can point you in the right direction.
 
My main question is : has someone succeed in using valgrind/memcheck on FreeBSD 12 ?

Thanks in advance !

Banging my drum again :)

Yes. It works fairly well for amd64 and GCC compiled executables. A bit less well for clang compiled executables, and also less well for i386. Of course, I'm not talking about the ports version.

Valgrind kind-of uses LD_PRELOAD, though it goes a lot further. It replaces ldrt and then redirects certain libc and libc++ functions to its own versions. It also filters all syscalls going to the kernel and also all signals. All of these (reading the ELF data from executables, changes to the memory layout, changes to syscalls) are liable to break Valgrind, which makes it fairly difficult to maintain.

C compilation and debuginfo are not necessary. The guest application does JIT compilation of blocks of machine code into its own format, VEX. That's the main reason why it is rather slow. You will see a bit more in the callstacks if you install base-dbg and/or libc32-dbg and the src packages.

The OP's example gives me the output below. The source lines for both the segfault and the leak are present. Maybe there should be more names from libc since I have both source and debuginfo installed.

Code:
==1625== Memcheck, a memory error detector
==1625== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1625== Using Valgrind-3.16.0.GIT-dbf811d86e-20200421X and LibVEX; rerun with -h for copyright info
==1625== Command: ./forum
==1625==
--1625-- Valgrind options:
--1625--    --tool=memcheck
--1625--    --leak-check=yes
--1625--    --leak-resolution=high
--1625--    --track-origins=yes
--1625--    --undef-value-errors=yes
--1625--    --show-leak-kinds=all
--1625--    --track-fds=yes
--1625--    --trace-children=no
--1625--    --vgdb=no
--1625--    --show-reachable=yes
--1625--    --verbose
--1625--    --error-exitcode=1
--1625-- Arch and hwcaps: AMD64, LittleEndian, amd64-cx16-rdtscp-sse3-ssse3
--1625-- Page sizes: currently 4096, max supported 4096
--1625-- Valgrind library directory: /home/paulf/tools/valgrind/lib/valgrind
--1625-- Reading syms from /usr/home/paulf/scratch/vg_examples/forum
--1625-- Reading syms from /libexec/ld-elf.so.1
--1625--   Considering /usr/lib/debug/libexec/ld-elf.so.1.debug ..
--1625--   .. CRC is valid
--1625--    object doesn't have a symbol table
--1625-- Reading syms from /usr/home/paulf/tools/valgrind/lib/valgrind/memcheck-amd64-freebsd
--1625-- ELF section outside all mapped regions
--1625-- Scheduler: using generic scheduler lock implementation.
--1625-- Reading suppressions file: /home/paulf/tools/valgrind/lib/valgrind/default.supp
--1625-- Reading syms from /usr/home/paulf/tools/valgrind/lib/valgrind/vgpreload_core-amd64-freebsd.so
--1625-- Reading syms from /usr/home/paulf/tools/valgrind/lib/valgrind/vgpreload_memcheck-amd64-freebsd.so
--1625-- Reading syms from /lib/libc.so.7
--1625--   Considering /usr/lib/debug/lib/libc.so.7.debug ..
--1625--   .. CRC is valid
--1625--    object doesn't have a symbol table
--1625-- REDIR: 0x4e0bf50 (libc.so.7:strncmp) redirected to 0x4a4e5a3 (strncmp)
--1625-- REDIR: 0x4e11380 (libc.so.7:memset) redirected to 0x4a50089 (memset)
--1625-- REDIR: 0x4d72990 (libc.so.7:malloc) redirected to 0x4a4a885 (malloc)
--1625-- REDIR: 0x4e0c070 (libc.so.7:strcpy) redirected to 0x4a4dc37 (strcpy)
--1625-- REDIR: 0x4e0bf90 (libc.so.7:strlen) redirected to 0x4a4dbaa (strlen)
--1625-- REDIR: 0x4e11490 (libc.so.7:memcpy) redirected to 0x4a4edec (memcpy)
--1625-- REDIR: 0x4d76730 (libc.so.7:free) redirected to 0x4a4b551 (free)
==1625== Invalid write of size 1
==1625==    at 0x201376: main (forum.c:18)
==1625==  Address 0xdeadbeef is not stack'd, malloc'd or (recently) free'd
==1625==
==1625==
==1625== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==1625==  Access not within mapped region at address 0xDEADBEEF
==1625==    at 0x201376: main (forum.c:18)
==1625==  If you believe this happened as a result of a stack
==1625==  overflow in your program's main thread (unlikely but
==1625==  possible), you can try to increase the size of the
==1625==  main thread stack using the --main-stacksize= flag.
==1625==  The main thread stack size used in this run was 16777216.
==1625==
==1625== FILE DESCRIPTORS: 4 open at exit.
==1625== Open file descriptor 268: local
==1625==    <inherited from parent>
==1625==
==1625== Open file descriptor 2: local
==1625==    <inherited from parent>
==1625==
==1625== Open file descriptor 1: local
==1625==    <inherited from parent>
==1625==
==1625== Open file descriptor 0: /dev/pts/2
==1625==    <inherited from parent>
==1625==
==1625==
==1625== HEAP SUMMARY:
==1625==     in use at exit: 8,202 bytes in 2 blocks
==1625==   total heap usage: 3 allocs, 1 frees, 8,244 bytes allocated
==1625==
==1625== Searching for pointers to 2 not-freed blocks
==1625== Checked 8,776,432 bytes
==1625==
==1625== 10 bytes in 1 blocks are still reachable in loss record 1 of 2
==1625==    at 0x4A4A924: malloc (vg_replace_malloc.c:312)
==1625==    by 0x2012FF: main (forum.c:7)
==1625==
==1625== 8,192 bytes in 1 blocks are still reachable in loss record 2 of 2
==1625==    at 0x4A4A924: malloc (vg_replace_malloc.c:312)
==1625==    by 0x4E0F722: ??? (in /lib/libc.so.7)
==1625==    by 0x4E0F628: ??? (in /lib/libc.so.7)
==1625==    by 0x4DA7415: ??? (in /lib/libc.so.7)
==1625==    by 0x4DA7184: vfprintf_l (in /lib/libc.so.7)
==1625==    by 0x4DF3203: printf (in /lib/libc.so.7)
==1625==    by 0x20132A: main (forum.c:10)
==1625==
==1625== LEAK SUMMARY:
==1625==    definitely lost: 0 bytes in 0 blocks
==1625==    indirectly lost: 0 bytes in 0 blocks
==1625==      possibly lost: 0 bytes in 0 blocks
==1625==    still reachable: 8,202 bytes in 2 blocks
==1625==         suppressed: 0 bytes in 0 blocks
==1625==
==1625== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
==1625==
==1625== 1 errors in context 1 of 1:
==1625== Invalid write of size 1
==1625==    at 0x201376: main (forum.c:18)
==1625==  Address 0xdeadbeef is not stack'd, malloc'd or (recently) free'd
==1625==
==1625== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
 
Back
Top