On Thu, Oct 24, 2024 at 2:00 PM Ilya Leoshkevich <i...@linux.ibm.com> wrote:

> Attaching to the gdbstub of a running process requires stopping its
> threads. For threads that run on a CPU, cpu_exit() is enough, but the
> only way to grab attention of a thread that is stuck in a long-running
> syscall is to interrupt it with a signal.
>
> Reserve a host realtime signal for this, just like it's already done
> for TARGET_SIGABRT on Linux. This may reduce the number of available
> guest realtime signals by one, but this is acceptable, since there are
> quite a lot of them, and it's unlikely that there are apps that need
> them all.
>
> Set signal_pending for the safe_sycall machinery to prevent invoking
> the syscall. This is a lie, since we don't queue a guest signal, but
> process_pending_signals() can handle the absence of pending signals.
> The syscall returns with QEMU_ERESTARTSYS errno, which arranges for
> the automatic restart. This is important, because it helps avoiding
> disturbing poorly written guests.
>
> Signed-off-by: Ilya Leoshkevich <i...@linux.ibm.com>
> ---
>  bsd-user/signal.c     | 12 ++++++++++++
>  include/user/signal.h |  2 ++
>  linux-user/signal.c   | 11 +++++++++++
>  3 files changed, 25 insertions(+)
>
> diff --git a/bsd-user/signal.c b/bsd-user/signal.c
> index a2b11a97131..992736df5c5 100644
> --- a/bsd-user/signal.c
> +++ b/bsd-user/signal.c
> @@ -49,6 +49,8 @@ static inline int sas_ss_flags(TaskState *ts, unsigned
> long sp)
>          on_sig_stack(ts, sp) ? SS_ONSTACK : 0;
>  }
>
> +int host_interrupt_signal = SIGRTMAX;
> +
>

I'd be tempted to use SIGRTMAX + 1 or even TARGET_NSIG. 127 or 128 would
work and not overflow any arrays (or hit any bounds tests) I'd likely use
SIGRTMAX + 1,
though, since it avoids any edge-cases from sig == NSIG that might be in
the code
unnoticed.

Now, having said that, I don't think that there's too many (any?) programs
we need
to run as bsd-user that have real-time signals, much less one that uses
SIGRTMAX,
but stranger things have happened. But it is a little wiggle room just in
case.

Other than that:

Reviewed-by: Warner Losh <i...@bsdimp.com>



>  /*
>   * The BSD ABIs use the same signal numbers across all the CPU
> architectures, so
>   * (unlike Linux) these functions are just the identity mapping. This
> might not
> @@ -489,6 +491,12 @@ static void host_signal_handler(int host_sig,
> siginfo_t *info, void *puc)
>      uintptr_t pc = 0;
>      bool sync_sig = false;
>
> +    if (host_sig == host_interrupt_signal) {
> +        ts->signal_pending = 1;
> +        cpu_exit(thread_cpu);
> +        return;
> +    }
> +
>      /*
>       * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special
>       * handling wrt signal blocking and unwinding.
> @@ -852,6 +860,9 @@ void signal_init(void)
>
>      for (i = 1; i <= TARGET_NSIG; i++) {
>          host_sig = target_to_host_signal(i);
> +        if (host_sig == host_interrupt_signal) {
> +            continue;
> +        }
>          sigaction(host_sig, NULL, &oact);
>          if (oact.sa_sigaction == (void *)SIG_IGN) {
>              sigact_table[i - 1]._sa_handler = TARGET_SIG_IGN;
> @@ -870,6 +881,7 @@ void signal_init(void)
>              sigaction(host_sig, &act, NULL);
>          }
>      }
> +    sigaction(host_interrupt_signal, &act, NULL);
>  }
>
>  static void handle_pending_signal(CPUArchState *env, int sig,
> diff --git a/include/user/signal.h b/include/user/signal.h
> index 19b6b9e5ddc..7fa33b05d91 100644
> --- a/include/user/signal.h
> +++ b/include/user/signal.h
> @@ -20,4 +20,6 @@
>   */
>  int target_to_host_signal(int sig);
>
> +extern int host_interrupt_signal;
> +
>  #endif
> diff --git a/linux-user/signal.c b/linux-user/signal.c
> index 84bb8a34808..f0bcbd367d5 100644
> --- a/linux-user/signal.c
> +++ b/linux-user/signal.c
> @@ -514,6 +514,8 @@ static int core_dump_signal(int sig)
>      }
>  }
>
> +int host_interrupt_signal;
> +
>  static void signal_table_init(void)
>  {
>      int hsig, tsig, count;
> @@ -540,6 +542,7 @@ static void signal_table_init(void)
>      hsig = SIGRTMIN;
>      host_to_target_signal_table[SIGABRT] = 0;
>      host_to_target_signal_table[hsig++] = TARGET_SIGABRT;
> +    host_interrupt_signal = hsig++;
>
>      for (tsig = TARGET_SIGRTMIN;
>           hsig <= SIGRTMAX && tsig <= TARGET_NSIG;
> @@ -619,6 +622,8 @@ void signal_init(void)
>          }
>          sigact_table[tsig - 1]._sa_handler = thand;
>      }
> +
> +    sigaction(host_interrupt_signal, &act, NULL);
>  }
>
>  /* Force a synchronously taken signal. The kernel force_sig() function
> @@ -966,6 +971,12 @@ static void host_signal_handler(int host_sig,
> siginfo_t *info, void *puc)
>      bool sync_sig = false;
>      void *sigmask;
>
> +    if (host_sig == host_interrupt_signal) {
> +        ts->signal_pending = 1;
> +        cpu_exit(thread_cpu);
> +        return;
> +    }
> +
>      /*
>       * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special
>       * handling wrt signal blocking and unwinding.  Non-spoofed SIGILL,
> --
> 2.47.0
>
>

Reply via email to