Bug 28623 - Missing catching return of execve syscall of PowerPC
Summary: Missing catching return of execve syscall of PowerPC
Status: NEW
Alias: None
Product: gdb
Classification: Unclassified
Component: tdep (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-11-24 19:27 UTC by Simon Marchi
Modified: 2023-11-23 14:53 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
Alternative solution (1.73 KB, patch)
2023-11-17 12:37 UTC, Tom de Vries
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Simon Marchi 2021-11-24 19:27:13 UTC
When catching the execve syscall on other architectures, such ash x86-64, we get an entry and return event:

    $ ./gdb -q --data-directory=data-directory m64
    Reading symbols from m64...
    (gdb) catch syscall execve
    Catchpoint 1 (syscall 'execve' [59])
    (gdb) r
    Starting program: /home/smarchi/build/binutils-gdb-all-targets/gdb/m64

    Catchpoint 1 (call to syscall execve), 0x00007ffff7ea92fb in execve () at ../sysdeps/unix/syscall-template.S:78
    78      ../sysdeps/unix/syscall-template.S: No such file or directory.
    (gdb) c
    Continuing.
    process 3592054 is executing new program: /home/smarchi/build/binutils-gdb-all-targets/gdb/m64

    Catchpoint 1 (returned from syscall execve), 0x00007ffff7fd0100 in _start () from /lib64/ld-linux-x86-64.so.2

On PowerPC, we don't get the return:

    $ ./gdb -q -nx --data-directory=data-directory ./a.out
    Reading symbols from ./a.out...
    (gdb) catch syscall execve
    Catchpoint 1 (syscall 'execve' [11])
    (gdb) r
    Starting program: /home/simark/build/binutils-gdb/gdb/a.out

    Catchpoint 1 (call to syscall execve), 0x00007ffff7e6f18c in execve () from /lib64/libc.so.6
    (gdb) c
    Continuing.
    process 98693 is executing new program: /home/simark/build/binutils-gdb/gdb/a.out

    Catchpoint 1 (call to syscall execve), 0x00007ffff7e6f18c in execve () from /lib64/libc.so.6
Comment 1 Tom de Vries 2023-11-16 10:30:47 UTC
We currently have an xfail for this in gdb.base/catch-syscall.exp.

With kernel version 3.10.0 and 4.18.0 I hit the xfail case.

The test passes with kernel version 5.14.0 though.

The problem is that the get_syscall_number call returns 0 at syscall exit (it returns 11 at syscall entry, as expected).

See the aarch64 version of get_syscall_number for a way to deal with this.

See also PR31071 for the arm variant of this.
Comment 2 Tom de Vries 2023-11-16 10:42:35 UTC
Using this demonstrator patch I can hit the syscall exit:
...
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index 0c676a8..a5cfb7d 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -1344,6 +1344,8 @@
   ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
 
+  return 11;
+
   /* Make sure we're in a 32- or 64-bit machine */
   gdb_assert (tdep->wordsize == 4 || tdep->wordsize == 8);
 
...
which allows me to sample the registers:
...
(gdb) info registers
r0             0x0                 0
r1             0x7fffffffee10      140737488350736
r2             0x0                 0
r3             0x0                 0
r4             0x0                 0
r5             0x0                 0
r6             0x0                 0
r7             0x0                 0
r8             0x0                 0
r9             0x0                 0
r10            0x0                 0
r11            0x0                 0
r12            0x7ffff7fb19e0      140737353816544
r13            0x0                 0
r14            0x0                 0
r15            0x0                 0
r16            0x0                 0
r17            0x0                 0
r18            0x0                 0
r19            0x0                 0
r20            0x0                 0
r21            0x0                 0
r22            0x0                 0
r23            0x0                 0
r24            0x0                 0
r25            0x0                 0
r26            0x0                 0
r27            0x0                 0
r28            0x0                 0
r29            0x0                 0
r30            0x0                 0
r31            0x0                 0
pc             0x7ffff7fb19e0      0x7ffff7fb19e0 <_start>
msr            0xb00000000000d033  12682136550675370035
cr             0x0                 0
lr             0x0                 0x0
ctr            0x0                 0
xer            0x0                 0
fpscr          0x0                 0
vscr           0x0                 0
vrsave         0xffffffff          -1
ppr            0xc000000000000     3377699720527872
dscr           0x10                16
tar            0x0                 0
bescr          <unavailable>
ebbhr          <unavailable>
ebbrr          <unavailable>
mmcr0          0x0                 0
mmcr2          0x0                 0
siar           0x0                 0
sdar           0x0                 0
sier           0x0                 0
orig_r3        0x7ffffffff1c5      140737488351685
trap           0xc00               3072
...

Looks like the lr == 0 approach might work here as well.
Comment 3 Tom de Vries 2023-11-16 11:48:22 UTC
Tentative patch implementing lr == 0 approach:
...
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index 0c676a8..057ae04 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -1355,7 +1355,19 @@
      is stored at 0th register.  */
   regcache->cooked_read (tdep->ppc_gp0_regnum, buf.data ());
 
-  return extract_signed_integer (buf.data (), tdep->wordsize, byte_order);
+  LONGEST res = extract_signed_integer (buf.data (), tdep->wordsize, byte_order);
+
+  if (res == 0)
+    {
+      regcache->cooked_read (tdep->ppc_lr_regnum, buf.data ());
+
+      if (extract_signed_integer (buf.data (), tdep->wordsize, byte_order) == 0)
+       return 11;
+
+      return -1;
+    }
+
+  return res;
 }
 
 /* PPC process record-replay */
...
Comment 4 Tom de Vries 2023-11-16 11:50:02 UTC
Ulrich, any comments on this approach ?
Comment 5 ulrich.weigand 2023-11-16 17:00:27 UTC
So if I get this correctly, the problem is that the execve syscall doesn't really "return" as such, but rather starts execution at the entry point of the new executable.  At this point, memory contents reflect the new executable and register context are reset to the default setting on startup.

This means that you cannot reliably detect that you previously executed execve, as that requires looking at memory and/or register contents (depending on platform ABI details).  I guess your suggested patch(es) are heuristics to work around that problem, but this seems somewhat fragile to me.

On the other hand, there is a reliable way to detect that we just executed an execve syscall, and that is the TARGET_WAITKIND_EXECD wait state.  Maybe the catch syscall logic, in the special case of catching execve, should somehow hook into that event instead?
Comment 6 Tom de Vries 2023-11-17 12:37:23 UTC
Created attachment 15223 [details]
Alternative solution
Comment 7 ulrich.weigand 2023-11-17 14:32:16 UTC
This is certainly better, but still somewhat fragile in that it still requires the platform back-end to reliably detect that it cannot detect the system call number.  On some targets (e.g. s390x) that read the system call number from memory as immediate operand of the syscall instruction, you may encounter the even worse scenario that after an execve you *do* detect a syscall but it is wrong (because at that same location in memory the new executable also contains a syscall instruction, but a different one) ...

I still think ideally we should be able to make use of the PTRACE_EVENT_EXEC reported by the kernel.  If I'm reading kernel code correctly, we should be getting the following sequence of ptrace events:
 - syscall SIGTRAP - before execve
 - PTRACE_EVENT_EXEC
 - syscall SIGTRAP - after execve

So maybe processing of PTRACE_EVENT_EXEC should set a flag that if syscall traps are active, the immediate next such event in the current process should not even attempt to call gdbarch_get_syscall_number, but simply assume that it is a return of the immediately preceding execve syscall.
Comment 8 Simon Marchi 2023-11-17 20:57:45 UTC
(In reply to ulrich.weigand from comment #7)
> This is certainly better, but still somewhat fragile in that it still
> requires the platform back-end to reliably detect that it cannot detect the
> system call number.  On some targets (e.g. s390x) that read the system call
> number from memory as immediate operand of the syscall instruction, you may
> encounter the even worse scenario that after an execve you *do* detect a
> syscall but it is wrong (because at that same location in memory the new
> executable also contains a syscall instruction, but a different one) ...

I agree with this concern.
 
> I still think ideally we should be able to make use of the PTRACE_EVENT_EXEC
> reported by the kernel.  If I'm reading kernel code correctly, we should be
> getting the following sequence of ptrace events:
>  - syscall SIGTRAP - before execve
>  - PTRACE_EVENT_EXEC
>  - syscall SIGTRAP - after execve
> 
> So maybe processing of PTRACE_EVENT_EXEC should set a flag that if syscall
> traps are active, the immediate next such event in the current process
> should not even attempt to call gdbarch_get_syscall_number, but simply
> assume that it is a return of the immediately preceding execve syscall.

I think that Tom was on a good path though, to save the syscall number at the syscall entry event and use it for the syscall exit event.  But instead of having an exception for execve, why not do that for all syscalls?

The only issue I could think of is what happens when you attach GDB to a thread while it is in a syscall, could it result in GDB receiving only a syscall exit event, without a prior syscall enter event?
Comment 9 Tom de Vries 2023-11-20 15:44:39 UTC
(In reply to ulrich.weigand from comment #7)
> This is certainly better, but still somewhat fragile in that it still
> requires the platform back-end to reliably detect that it cannot detect the
> system call number.  On some targets (e.g. s390x) that read the system call
> number from memory as immediate operand of the syscall instruction, you may
> encounter the even worse scenario that after an execve you *do* detect a
> syscall but it is wrong (because at that same location in memory the new
> executable also contains a syscall instruction, but a different one) ...
> 

I've updated the patch such that the cached value always overrides the gdbarch_get_syscall_number result, so that shouldn't be an issue anymore.

[ Btw, I've looked at s390_linux_get_syscall_number and observed the same problem as for arm: accessing memory in a way that may throw memory errors.  Instead the safe_ memory access variants should be used, and -1 returned. ]

> I still think ideally we should be able to make use of the PTRACE_EVENT_EXEC
> reported by the kernel.  If I'm reading kernel code correctly, we should be
> getting the following sequence of ptrace events:
>  - syscall SIGTRAP - before execve
>  - PTRACE_EVENT_EXEC
>  - syscall SIGTRAP - after execve
> 
> So maybe processing of PTRACE_EVENT_EXEC should set a flag that if syscall
> traps are active, the immediate next such event in the current process
> should not even attempt to call gdbarch_get_syscall_number, but simply
> assume that it is a return of the immediately preceding execve syscall.

I've implemented this as a followup patch on the caching one.

I've posted an RFC here ( https://sourceware.org/pipermail/gdb-patches/2023-November/204275.html ).
Comment 10 Tom de Vries 2023-11-20 15:49:36 UTC
(In reply to Simon Marchi from comment #8)
> I think that Tom was on a good path though, to save the syscall number at
> the syscall entry event and use it for the syscall exit event.  But instead
> of having an exception for execve, why not do that for all syscalls?
> 

The RFC I've posted only does it for execve (and only for powerpc for now as well).

I suppose a clone event does not map uniquely to a system call, maybe it could be either clone, clone2 or clone3?

> The only issue I could think of is what happens when you attach GDB to a
> thread while it is in a syscall, could it result in GDB receiving only a
> syscall exit event, without a prior syscall enter event?

In the RFC, in the first patch I've mentioned a scenario (not using attach though) where we can get a syscall exit event without observing a prior syscall enter event.
Comment 11 Tom de Vries 2023-11-21 12:42:57 UTC
(In reply to Tom de Vries from comment #9)
> [ Btw, I've looked at s390_linux_get_syscall_number and observed the same
> problem as for arm: accessing memory in a way that may throw memory errors. 
> Instead the safe_ memory access variants should be used, and -1 returned. ]

https://sourceware.org/pipermail/gdb-patches/2023-November/204354.html