Bug 1927153

Summary: -flto=auto makes valgrind report non-existing paths to source files
Product: [Fedora] Fedora Reporter: Kamil Dudka <kdudka>
Component: valgrindAssignee: Mark Wielaard <mjw>
Status: CLOSED CURRENTRELEASE QA Contact: Fedora Extras Quality Assurance <extras-qa>
Severity: medium Docs Contact:
Priority: unspecified    
Version: rawhideCC: dodji, jakub, kdudka, mjw
Target Milestone: ---   
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: valgrind-3.16.1-19.fc34 Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2021-02-21 23:57:29 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Kamil Dudka 2021-02-10 08:58:47 UTC
Description of problem:
When code is compiled/linked with `-O2 -flto=auto`, valgrind reports memory handling errors with non-existing paths to source files.


Version-Release number of selected component (if applicable):
binutils-2.35.1-33.fc34.x86_64
gcc-11.0.0-0.18.fc34.x86_64
valgrind-3.16.1-18.fc34.x86_64


Steps to Reproduce:
1. Prepare a pair of source/build directories:
$ mkdir -p /tmp/test-valgrind/build
$ cd /tmp/test-valgrind
$ echo 'void foo(){}' > empty-fn.c
$ echo 'int main() {free(main); foo();}' > bad-free.c
$ cd build

2. Without `-flto=auto`, valgrind's output is correct:
$ gcc -g -w -O2 ../{bad-free,empty-fn}.c && valgrind --xml=yes --xml-file=>(grep '<dir') ./a.out
      <dir>/builddir/build/BUILD/valgrind-3.16.1/coregrind/m_replacemalloc</dir>
      <dir>/tmp/test-valgrind/build/..</dir>
      <dir>/tmp/test-valgrind/build/..</dir>


3. With `-flto=auto`, valgrind's output is incorrect:
$ gcc -g -w -O2 -flto=auto ../{bad-free,empty-fn}.c && valgrind --xml=yes --xml-file=>(grep '<dir') ./a.out
      <dir>/builddir/build/BUILD/valgrind-3.16.1/coregrind/m_replacemalloc</dir>
      <dir>/tmp/test-valgrind/build</dir>
      <dir>/tmp/test-valgrind/build</dir>


Additional info:
This causes problems with automated dynamic analysis of source RPM packages because -flto=auto is used in default build flags.

Comment 1 Mark Wielaard 2021-02-10 10:00:10 UTC
Does this happen only with XML output or do you also see this with:

valgrind -q --fullpath-after="" ./a.out

(The --fullpath-after="" shows the full path, default is to only show the file name.)

I cannot replicate on f33, so this might be related to the combination of gcc 11 defaulting to DWARF5 and LTO.

Comment 2 Kamil Dudka 2021-02-10 12:24:21 UTC
(In reply to Mark Wielaard from comment #1)
> Does this happen only with XML output or do you also see this with:
> 
> valgrind -q --fullpath-after="" ./a.out
> 
> (The --fullpath-after="" shows the full path, default is to only show the
> file name.)

Same problem with `valgrind -q --fullpath-after=""`:

$ gcc -g -w -O2 ../{bad-free,empty-fn}.c && valgrind -q --fullpath-after="" ./a.out
==852== Invalid free() / delete / delete[] / realloc()
==852==    at 0x483E9F1: free (/builddir/build/BUILD/valgrind-3.16.1/coregrind/m_replacemalloc/vg_replace_malloc.c:538)
==852==    by 0x40104D: main (/tmp/test-valgrind/build/../bad-free.c:1)
==852==  Address 0x401040 is in the Text segment of /tmp/test-valgrind/build/a.out
==852==    at 0x401040: main (/tmp/test-valgrind/build/../bad-free.c:1)
==852==

$ gcc -g -w -O2 -flto=auto ../{bad-free,empty-fn}.c && valgrind -q --fullpath-after="" ./a.out
==870== Invalid free() / delete / delete[] / realloc()
==870==    at 0x483E9F1: free (/builddir/build/BUILD/valgrind-3.16.1/coregrind/m_replacemalloc/vg_replace_malloc.c:538)
==870==    by 0x40104D: main (/tmp/test-valgrind/build/bad-free.c:1)
==870==  Address 0x401040 is in the Text segment of /tmp/test-valgrind/build/a.out
==870==    at 0x401040: main (/tmp/test-valgrind/build/bad-free.c:1)
==870==

> I cannot replicate on f33

I confirm the problem is not observable on f33 with the above steps to reproduce.

Comment 3 Kamil Dudka 2021-02-10 13:03:35 UTC
Note that gdb prints paths to source files correctly for the same binary:

$ valgrind -q --fullpath-after="" ./a.out
==985== Invalid free() / delete / delete[] / realloc()
==985==    at 0x483E9F1: free (/builddir/build/BUILD/valgrind-3.16.1/coregrind/m_replacemalloc/vg_replace_malloc.c:538)
==985==    by 0x40104D: main (/tmp/test-valgrind/build/bad-free.c:1)
==985==  Address 0x401040 is in the Text segment of /tmp/test-valgrind/build/a.out
==985==    at 0x401040: main (/tmp/test-valgrind/build/bad-free.c:1)
==985== 

$ rpm -q gdb
gdb-10.1-4.fc34.x86_64

$ echo info sources | gdb -q ./a.out
Reading symbols from ./a.out...
(gdb) Source files for which symbols have been read in:



Source files for which symbols will be read in on demand:

/tmp/test-valgrind/empty-fn.c, /tmp/test-valgrind/bad-free.c, /tmp/test-valgrind/build/<artificial>@0x0

Comment 4 Kamil Dudka 2021-02-10 13:07:10 UTC
$ gdb -q ./a.out
Reading symbols from ./a.out...
(gdb) start
Temporary breakpoint 1 at 0x401040: file ../bad-free.c, line 1.
Starting program: /tmp/test-valgrind/build/a.out 

Temporary breakpoint 1, main () at ../bad-free.c:1
1       int main() {free(main); foo();}

Comment 5 Mark Wielaard 2021-02-21 20:36:30 UTC
The directory table looks as follows:

Directory table:
      [path(line_strp)]
 0     /tmp/test-valgrind/build (0)
 1     .. (38)

File name table:
      [path(line_strp), directory_index(udata)]
 0     <artificial> (25),  0
 1     empty-fn.c (69),  1
 2     bad-free.c (55),  1
 3     <built-in> (41),  0

So we are looking at file name 2 (bad-free.c), with directory 1 (..) and compdir /tmp/test-valgrind/build
For some reason valgrind drops the .. (dir 1) part.

Comment 6 Mark Wielaard 2021-02-21 21:40:13 UTC
Found it, it was a typo in the valgrind DWARF5 line table reader:

diff --git a/coregrind/m_debuginfo/readdwarf.c b/coregrind/m_debuginfo/readdwarf.c
index 88d5d99f1..3996623ed 100644
--- a/coregrind/m_debuginfo/readdwarf.c
+++ b/coregrind/m_debuginfo/readdwarf.c
@@ -796,7 +796,7 @@ void read_dwarf2_lineblock ( struct _DebugInfo* di,
             if (f == p_ndx)
                name = get_line_str (di, ui, &data, form,
                                     debugstr_img, debuglinestr_img);
-            else if (n == d_ndx)
+            else if (f == d_ndx)
                diridx = get_line_ndx (di, &data, form);
             else
                data = skip_line_form (di, ui, data, form);

That meant it got the dir index wrong, in this case it believed the dir index was zero instead of one.

Comment 8 Kamil Dudka 2021-02-22 09:29:31 UTC
I confirm that valgrind-3.16.1-19.fc35 works as expected.  Thank you for fixing it!