TL;DR: Old versions of Valgrind detect false positive memory issues in SDL2 applications. This was fixed in Valgrind v3.20, but unfortunately v3.19 is the latest available version on Debian stable apt
. A solution is to build & install Valgrind from source .
Problem § With Valgrind v3.19, my SDL2 C application always gets “Invalid read of size 8” errors in strncmp
at the start:
$ valgrind --track-origins= yes --leak-check= full ./build/app/blah
==35960== Memcheck, a memory error detector
==35960== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==35960== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==35960== Command: ./build/app/blah
==35960== Parent PID: 2836
==35960==
==35960== Invalid read of size 8
==35960== at 0x4023AB4: strncmp (strcmp-sse2.S:162)
==35960== by 0x4004B9E: is_dst (dl-load.c:216)
==35960== by 0x400596E: _dl_dst_count (dl-load.c:253)
==35960== by 0x4005B47: expand_dynamic_string_token (dl-load.c:395)
==35960== by 0x4005CB2: fillin_rpath.isra.0 (dl-load.c:483)
==35960== by 0x4005FA2: decompose_rpath (dl-load.c:654)
==35960== by 0x400815B: _dl_map_object (dl-load.c:2111)
==35960== by 0x4002260: openaux (dl-deps.c:64)
==35960== by 0x4BCAFC9: _dl_catch_exception (dl-error-skeleton.c:208)
==35960== by 0x40025C9: _dl_map_object_deps (dl-deps.c:232)
==35960== by 0x400BA6C: dl_open_worker_begin (dl-open.c:592)
==35960== by 0x4BCAFC9: _dl_catch_exception (dl-error-skeleton.c:208)
==35960== Address 0x6d10559 is 9 bytes inside a block of size 15 alloc'd
==35960== at 0x48407B4: malloc (vg_replace_malloc.c:381)
==35960== by 0x402389A: malloc (rtld-malloc.h:56)
==35960== by 0x402389A: strdup (strdup.c:42)
==35960== by 0x4005F34: decompose_rpath (dl-load.c:629)
==35960== by 0x400815B: _dl_map_object (dl-load.c:2111)
==35960== by 0x4002260: openaux (dl-deps.c:64)
==35960== by 0x4BCAFC9: _dl_catch_exception (dl-error-skeleton.c:208)
==35960== by 0x40025C9: _dl_map_object_deps (dl-deps.c:232)
==35960== by 0x400BA6C: dl_open_worker_begin (dl-open.c:592)
==35960== by 0x4BCAFC9: _dl_catch_exception (dl-error-skeleton.c:208)
==35960== by 0x400B1C5: dl_open_worker (dl-open.c:782)
==35960== by 0x4BCAFC9: _dl_catch_exception (dl-error-skeleton.c:208)
==35960== by 0x400B5B7: _dl_open (dl-open.c:884)
==35960==
==35960== Invalid read of size 8
==35960== at 0x4023AB4: strncmp (strcmp-sse2.S:162)
==35960== by 0x4004B9E: is_dst (dl-load.c:216)
==35960== by 0x4005A0D: _dl_dst_substitute (dl-load.c:295)
==35960== by 0x4005CB2: fillin_rpath.isra.0 (dl-load.c:483)
==35960== by 0x4005FA2: decompose_rpath (dl-load.c:654)
==35960== by 0x400815B: _dl_map_object (dl-load.c:2111)
==35960== by 0x4002260: openaux (dl-deps.c:64)
==35960== by 0x4BCAFC9: _dl_catch_exception (dl-error-skeleton.c:208)
==35960== by 0x40025C9: _dl_map_object_deps (dl-deps.c:232)
==35960== by 0x400BA6C: dl_open_worker_begin (dl-open.c:592)
==35960== by 0x4BCAFC9: _dl_catch_exception (dl-error-skeleton.c:208)
==35960== by 0x400B1C5: dl_open_worker (dl-open.c:782)
==35960== Address 0x6d10559 is 9 bytes inside a block of size 15 alloc'd
==35960== at 0x48407B4: malloc (vg_replace_malloc.c:381)
==35960== by 0x402389A: malloc (rtld-malloc.h:56)
==35960== by 0x402389A: strdup (strdup.c:42)
==35960== by 0x4005F34: decompose_rpath (dl-load.c:629)
==35960== by 0x400815B: _dl_map_object (dl-load.c:2111)
==35960== by 0x4002260: openaux (dl-deps.c:64)
==35960== by 0x4BCAFC9: _dl_catch_exception (dl-error-skeleton.c:208)
==35960== by 0x40025C9: _dl_map_object_deps (dl-deps.c:232)
==35960== by 0x400BA6C: dl_open_worker_begin (dl-open.c:592)
==35960== by 0x4BCAFC9: _dl_catch_exception (dl-error-skeleton.c:208)
==35960== by 0x400B1C5: dl_open_worker (dl-open.c:782)
==35960== by 0x4BCAFC9: _dl_catch_exception (dl-error-skeleton.c:208)
==35960== by 0x400B5B7: _dl_open (dl-open.c:884)
Investigation § I found this question with the exact same issue. This answer says it is an issue with Valgrind that was fixed in v3.20 .
$ valgrind --version
valgrind-3.19.0
I am using Valgrind v3.19, so that may explain why I get this issue.
Build & install Valgrind from source § I am running Debian, and my currently installed Valgrind was from apt
. Unfortunately v3.19 is the latest version available on apt
.
In order to get a newer version of Valgrind I’ll have to install it from source.
Building Valgrind from source § First let’s uninstall Valgrind from apt
:
$ sudo apt remove valgrind
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following packages will be REMOVED:
valgrind valgrind-dbg
0 upgraded, 0 newly installed, 2 to remove and 0 not upgraded.
After this operation, 149 MB disk space will be freed.
Do you want to continue? [Y/n] Y
(Reading database ... 396286 files and directories currently installed.)
Removing valgrind-dbg (1:3.19.0-1) ...
Removing valgrind (1:3.19.0-1) ...
Processing triggers for man-db (2.11.2-2) ...
Then follow the official instructions to install from source . It’s pretty simple:
$ git clone https://sourceware.org/git/valgrind.git
$ cd valgrind
$ ./autogen.sh
$ ./configure
$ make
$ sudo make install
This outputs a vg-in-place
binary. We can run this in place, e.g. ./vg-in-place
, but it would be better if the valgrind
command were available everywhere in the shell.
Installing Valgrind from source § To finish the install, we copy vg-in-place
to /usr/bin
and rename it to valgrind
:
$ sudo cp vg-in-place /usr/bin/valgrind
Now valgrind
is on the path:
$ valgrind --version
valgrind-3.25.0.GIT
Looks good, we’ve got v3.25.0!
Rerun § Let’s check if that worked by running the same command as before:
$ valgrind --track-origins= yes --leak-check= full ./build/app/blah
==84851== Memcheck, a memory error detector
==84851== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==84851== Using Valgrind-3.25.0.GIT and LibVEX; rerun with -h for copyright info
==84851== Command: ./build/app/blah
==84851== Parent PID: 84697
==84851==
==84851==
==84851== HEAP SUMMARY:
==84851== in use at exit: 793,022 bytes in 3,500 blocks
==84851== total heap usage: 84,343 allocs, 80,843 frees, 71,050,060 bytes allocated
==84851==
==84851== LEAK SUMMARY:
==84851== definitely lost: 0 bytes in 0 blocks
==84851== indirectly lost: 0 bytes in 0 blocks
==84851== possibly lost: 0 bytes in 0 blocks
==84851== still reachable: 793,022 bytes in 3,500 blocks
==84851== suppressed: 0 bytes in 0 blocks
==84851== Reachable blocks (those to which a pointer was found) are not shown.
==84851== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==84851==
==84851== For lists of detected and suppressed errors, rerun with: -s
==84851== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
No more “Invalid read” errors.
That’s fixed it, great! 🎉