smhk

Valgrind 'Invalid read' with SDL2

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.201.

$ 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! 🎉


  1. Specifically, it was fixed by this commit↩︎