Bug 124050 - libgcc unwinder incorrectly handles signal frames without the machine-specific fallback
Summary: libgcc unwinder incorrectly handles signal frames without the machine-specifi...
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: libgcc (show other bugs)
Version: 16.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2026-02-10 08:33 UTC by Xi Ruoyao
Modified: 2026-02-25 13:42 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2026-02-10 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Xi Ruoyao 2026-02-10 08:33:42 UTC
I've discussed this issue with H. Peter Anvin via the kernel list, beginning at https://lore.kernel.org/all/f3412cc3e8f66d1853cc9d572c0f2fab076872b1.camel@xry111.site/.

The code current reads:

  fde = _Unwind_Find_FDE (context->ra + _Unwind_IsSignalFrame (context) - 1,
                          &context->bases);
  if (fde == NULL)
    {
#ifdef MD_FALLBACK_FRAME_STATE_FOR
      /* Couldn't find frame unwind info for this function.  Try a
         target-specific fallback mechanism.  This will necessarily
         not provide a personality routine or LSDA.  */
      return MD_FALLBACK_FRAME_STATE_FOR (context, fs);
#else
      return _URC_END_OF_STACK;
#endif
    }

  fs->pc = context->bases.func;

  cie = get_cie (fde);
  insn = extract_cie_info (cie, context, fs);

It indeed attempts to avoid subtracting 1 for a signal frame, but _Unwind_IsSignalFrame (context) actually extracts a flag in context
which will only be raised up by extract_cie_info.  So the logic never worked.

On many existing Linux targets, the unwinding from signal handler actually works by using MD_FALLBACK_FRAME_STATE_FOR (sometimes only coincidentally).  For example, on AArch64 and LoongArch it's simply the unwind info isn't emitted for vDSO as at now, if the kernel developers want to add the unwind info for them, they still need a hack (inserting a NOP before the sigreturn trampoline, x86 is already doing this) for unwinding from signal frame.
Comment 1 Eric Botcazou 2026-02-10 11:18:23 UTC
This very likely worked by means to MD_FROB_UPDATE_CONTEXT?
Comment 2 H. Peter Anvin 2026-02-10 20:31:41 UTC
Is there any kind of tool we can use to test?
Comment 3 Jakub Jelinek 2026-02-10 21:42:00 UTC
I thought these days most of the time signal frames work through glibc using the right unwind info for the trampolines.
sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c on x86_64 etc.
MD_FALLBACK_FRAME_STATE_FOR just kicks in when that isn't provided.
Comment 4 H. Peter Anvin 2026-02-10 22:06:37 UTC
That's what SHOULD happen. The big question is if that is what DOES happen.
Comment 5 H. Peter Anvin 2026-02-10 22:07:39 UTC
It isn't just glibc, either; on legacy platforms that used to use the stack it is handled by the vdso these days.

The real question is: is the signal frame flag actually picked up, or not?
Comment 6 Eric Botcazou 2026-02-10 23:18:04 UTC
> The real question is: is the signal frame flag actually picked up, or not?

Yes, we would need a testcase here (hence the WAITING status).
Comment 7 Xi Ruoyao 2026-02-25 10:36:08 UTC
At the very least the situation is not so good on RISC-V.  With a simplified glibc test case:

#include <execinfo.h>
#include <inttypes.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>

static void
handler (int signal, siginfo_t *info, void *ctx)
{
  void *callstack[10];
  int callstack_count = backtrace (callstack, 10);
  bool found = false;
  for (int i = 0; i < callstack_count; ++i)
    printf ("info: call stack entry %d: 0x%" PRIxPTR "\n",
            i, (uintptr_t) callstack[i]);
}

int
main (void)
{
  struct sigaction sa =
    {
     .sa_sigaction = &handler,
     .sa_flags = SA_SIGINFO
    };
  sigaction (SIGUSR1, &sa, NULL);
  raise (SIGUSR1);
  return 0;
}

Debugging it on RISC-V shows:

(gdb) b _Unwind_Find_FDE
❌️ Function "_Unwind_Find_FDE" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (_Unwind_Find_FDE) pending.
(gdb) r 
Starting program: /home/xry111/a.out 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".

Program received signal SIGUSR1, User defined signal 1.
__pthread_kill_implementation (threadid=<optimized out>, signo=-877095168, 
    no_tid=0) at pthread_kill.c:44
⚠️ warning: 44	pthread_kill.c: no such file or directory
(gdb) c
Continuing.

Breakpoint 1, _Unwind_Find_FDE (pc=0x3ff7e1b323 <_Unwind_Backtrace+131>, 
    bases=bases@entry=0x3fffffe4b8) at ../../../libgcc/unwind-dw2-fde-dip.c:541
⚠️ warning: 541	../../../libgcc/unwind-dw2-fde-dip.c: no such file or directory
(gdb) c
Continuing.

Breakpoint 1, _Unwind_Find_FDE (pc=0x3ff7f20251 <__GI___backtrace+81>, 
    bases=bases@entry=0x3fffffe4b8) at ../../../libgcc/unwind-dw2-fde-dip.c:541
541	in ../../../libgcc/unwind-dw2-fde-dip.c
(gdb) c
Continuing.

Breakpoint 1, _Unwind_Find_FDE (pc=0x2aaaaaa87d <handler+49>, 
    bases=bases@entry=0x3fffffe4b8) at ../../../libgcc/unwind-dw2-fde-dip.c:541
541	in ../../../libgcc/unwind-dw2-fde-dip.c
(gdb) c
Continuing.

Breakpoint 1, _Unwind_Find_FDE (pc=0x3ff7fd358f, 
    bases=bases@entry=0x3fffffe4b8) at ../../../libgcc/unwind-dw2-fde-dip.c:541
541	in ../../../libgcc/unwind-dw2-fde-dip.c
(gdb) p pc+1
$1 = (void *) 0x3ff7fd3590 <__vdso_rt_sigreturn>
(gdb) finish
Run till exit from #0  _Unwind_Find_FDE (pc=0x3ff7fd358f, 
    bases=bases@entry=0x3fffffe4b8) at ../../../libgcc/unwind-dw2-fde-dip.c:541
0x0000003ff7e18af2 in uw_frame_state_for (context=context@entry=0x3fffffe090, 
    fs=fs@entry=0x3fffffe570) at ../../../libgcc/unwind-dw2.c:1008
⚠️ warning: 1008	../../../libgcc/unwind-dw2.c: no such file or directory
Value returned is $2 = (const fde *) 0x0

We can see the pc passed to _Unwind_Find_FDE is subtracted with 1, and _Unwind_Find_FDE cannot find the FDE.  Thus it relies on MD_FALLBACK_FRAME_STATE_FOR.

To make the situation more fragile: the fall back only works because on RISC-V __vdso_rt_sigreturn happens to be the first function in the text section.  Otherwise using the subtracted pc will cause _Unwind_Find_FDE to return the FDE of the previous function and break unwinding completely.