https://bugs.kde.org/show_bug.cgi?id=446123

            Bug ID: 446123
           Summary: Segfault when running a program that calls backtrace()
                    [ppc64le]
           Product: valgrind
           Version: 3.18.1
          Platform: Other
                OS: Linux
            Status: REPORTED
          Severity: normal
          Priority: NOR
         Component: general
          Assignee: jsew...@acm.org
          Reporter: jch...@redhat.com
  Target Milestone: ---

Packages used:
valgrind-3.18.1-1.el9.ppc64le
gcc-11.2.1-6.el9.ppc64le
glibc-2.34-8.el9.ppc64le
kernel-5.14.0-17.el9.ppc64le

Reproducer:
--------------------------------
#include <stdio.h>
#include <execinfo.h>

void call_backtrace(){
        void * callstack[128];
        backtrace(callstack, 128);
}

int main(){
    call_backtrace();
    return 0;
}
--------------------------------

Description of the problem:

I'm seeing a segfault when running a program that calls backtrace() under
valgrind, but not when I run it standalone. The segfault is ppc64le specific, I
doesn't reproduce in other platforms. It seems related to the fact that
valgrind removes the vDSO from the process, but glibc is still checking symbols
related to it.
The glibc code related to the segfault is the following
(sysdeps/powerpc/powerpc64/backtrace.c)
--------------------------------
67      static inline bool
68      is_sigtramp_address (void *nip)
69      {
70      #ifdef HAVE_SIGTRAMP_RT64
71        if (nip == GLRO (dl_vdso_sigtramp_rt64) ||
72            nip == GLRO (dl_vdso_sigtramp_rt64) + 4)
73          return true;
74      #endif
75        return false;
76      }
77
78      int
79      __backtrace (void **array, int size)
80      {
81        struct layout *current;
82        int count;
83
84        /* Force gcc to spill LR.  */
85        asm volatile ("" : "=l"(current));
86
87        /* Get the address on top-of-stack.  */
88        asm volatile ("ld %0,0(1)" : "=r"(current));
89
90        for (                         count = 0;
91             current != NULL &&       count < size;
92             current = current->next, count++)
93          {
94            array[count] = current->return_address;
95
96            /* Check if the symbol is the signal trampoline and get the
interrupted
97             * symbol address from the trampoline saved area.  */
98            if (is_sigtramp_address (current->return_address))
99              {
100               struct signal_frame_64 *sigframe = (struct signal_frame_64*)
current;
101               if (count + 1 == size)
102                 break;
103               array[++count] = (void*)
sigframe->uc.uc_mcontext.gp_regs[PT_NIP];
104               current = (void*) sigframe->uc.uc_mcontext.gp_regs[PT_R1];
105             }
106         }
----------------------------------------------------------------------

In each stack frame the function checks if the return address corresponds to
the signal trampoline by calling is_sigtramp_address (lines 98, 71).
When the program reaches the first of the frames, the return_address is 0x0.
Since valgrind is not holding any vDSO information, the symbol that holds the
signal trampoline is NULL, which causes that is_sigtramp_address returns true
(when it should return false). Then the block in line 99 is executed, filling
"current" with invalid data that will point to invalid memory and cause the
segfault. I ran this using gdb/vgdb:

(gdb) target remote | vgdb
Remote debugging using | vgdb
relaying data between gdb and process 33117
warning: remote target does not support file transfer, attempting to access
files from local filesystem.
Reading symbols from /lib64/ld64.so.2...
0x00000000040016e0 in _start () from /lib64/ld64.so.2
(gdb) b backtrace.c:94
No source file named backtrace.c.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (backtrace.c:94) pending.
(gdb) c
Continuing.

Breakpoint 1, __GI___backtrace (array=0x1fff00e540, size=128) at
../sysdeps/powerpc/powerpc64/backtrace.c:94
94            array[count] = current->return_address;
(gdb) p current->return_address
$1 = (void *) 0x10000678 <call_backtrace+44>
(gdb) c
Continuing.

Breakpoint 1, __GI___backtrace (array=0x1fff00e540, size=128) at
../sysdeps/powerpc/powerpc64/backtrace.c:94
94            array[count] = current->return_address;
(gdb) p current->return_address
$2 = (void *) 0x100006c0 <main+32>
(gdb) c
Continuing.

Breakpoint 1, __GI___backtrace (array=0x1fff00e540, size=128) at
../sysdeps/powerpc/powerpc64/backtrace.c:94
94            array[count] = current->return_address;
(gdb) p current->return_address
$3 = (void *) 0x4127ca4 <__libc_start_call_main+148>
(gdb) c
Continuing.

Breakpoint 1, __GI___backtrace (array=0x1fff00e540, size=128) at
../sysdeps/powerpc/powerpc64/backtrace.c:94
94            array[count] = current->return_address;
(gdb) p current->return_address
$4 = (void *) 0x4127e80 <__libc_start_main_impl+336>
(gdb) c
Continuing.

Breakpoint 1, __GI___backtrace (array=0x1fff00e540, size=128) at
../sysdeps/powerpc/powerpc64/backtrace.c:94
94            array[count] = current->return_address;
(gdb) p current->return_address
$5 = (void *) 0x0
(gdb) n
98            if (is_sigtramp_address (current->return_address))
(gdb) s
0x0000000004263b08 in is_sigtramp_address (nip=<optimized out>) at
../sysdeps/powerpc/powerpc64/backtrace.c:71
71        if (nip == GLRO (dl_vdso_sigtramp_rt64) ||
(gdb) s
98            if (is_sigtramp_address (current->return_address))
(gdb) n
103               array[++count] = (void*)
sigframe->uc.uc_mcontext.gp_regs[PT_NIP];
(gdb) n
92             current = current->next, count++)
(gdb) n
91             current != NULL &&       count < size;
(gdb) n

Breakpoint 1, __GI___backtrace (array=0x1fff00e540, size=128) at
../sysdeps/powerpc/powerpc64/backtrace.c:94
94            array[count] = current->return_address;
(gdb) p current->return_address
Cannot access memory at address 0x525f52454b414552

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to