Hi,

On Wed, 2025-07-02 at 00:04 +0200, Thomas Weißschuh wrote:
> [SNIP]
> > --- a/tools/include/nolibc/arch-i386.h
> > +++ b/tools/include/nolibc/arch-i386.h
> > @@ -10,6 +10,19 @@
> >  #include "compiler.h"
> >  #include "crt.h"
> >  
> > +/* Needed to get the correct struct sigaction definition */
> > +#define SA_RESTORER        0x04000000
> > +
> > +/* Restorer must be set on i386 */
> > +#define _NOLIBC_ARCH_NEEDS_SA_RESTORER
> > +
> > +/* Otherwise we would need to use sigreturn instead of rt_sigreturn */
> > +#define _NOLIBC_ARCH_FORCE_SIG_FLAGS SA_SIGINFO
> > +
> > +/* Avoid linux/signal.h, it has an incorrect _NSIG and sigset_t */
> > +#include <asm-generic/signal.h>
> > +#include <asm-generic/siginfo.h>
> 
> This doesn't work if the user already has <linux/signal.h> included for
> some other reason. The symbol names will conflict.

I was thinking this is fine. Such a conflict already exists between the
normal glibc <signal.h> and <linux/signal.h>. So there would only be a
problem if the user is explicitly not including <signal.h> to then use
<linux/signal.h>. I doubt that makes sense.

Benjamin

> 
> Can we do something like this:
> 
> #include <linux/signal.h>
> 
> /* lifted from asm-generic/signal.h */
> struct __nolibc_sigaction {
>       __sighandler_t sa_handler;
>       unsigned long sa_flags;
> #ifdef SA_RESTORER
>       __sigrestore_t sa_restorer;
> #endif
>       __nolibc_sigset_t sa_mask;
> };
> 
> int sys_rt_sigaction(int signum, const struct sigaction *act, struct 
> sigaction *oldact)
> {
>       struct __nolibc_sigaction nolibc_act, nolibc_oldact;
>       int ret;
> 
>       /* Convert whatever gunk we got from linux/signal.h to what the
>        * kernel actually expects. If it is the same structure,
>        * hopefully the compiler manages to optimize it away.
>        * See __nolibc_user_timespec_to_kernel() et al.
>        * (Comment not meant to be copied verbatim)
>        */
>       __nolibc_sigaction_user_to_kernel(act, &nolibc_act);
> 
> #if defined(_NOLIBC_ARCH_NEEDS_SA_RESTORER)
> ...
> #endif
> 
>       ret = my_syscall4(__NR_rt_sigaction, signum, &nolibc_act, 
> &nolibc_oldact, sizeof(nolibc_act->sa_mask));
> 
>       __nolibc_sigaction_kernel_to_user(&nolibc_oldact, oldact);
> 
>       return ret;
> }
> 
> Or am I missing something?
> 
> > +
> >  /* Syscalls for i386 :
> >   *   - mostly similar to x86_64
> >   *   - registers are 32-bit
> 
> <snip>
> 
> > --- a/tools/include/nolibc/arch-sparc.h
> > +++ b/tools/include/nolibc/arch-sparc.h
> > @@ -12,6 +12,19 @@
> >  #include "compiler.h"
> >  #include "crt.h"
> >  
> > +/* Otherwise we would need to use sigreturn instead of rt_sigreturn */
> > +#define _NOLIBC_ARCH_FORCE_SIG_FLAGS SA_SIGINFO
> > +
> > +/* The includes are sane, if one sets __WANT_POSIX1B_SIGNALS__ */
> > +#define __WANT_POSIX1B_SIGNALS__
> > +#include <linux/signal.h>
> 
> This also assumes the user did not already include <linux/signal.h>
> before including nolibc.
> 
> > +
> > +/*
> > + * sparc has ODD_RT_SIGACTION, we always pass our restorer as an argument
> > + * to rt_sigaction. The restorer is implemented in this file.
> > + */
> > +#define _NOLIBC_RT_SIGACTION_PASSES_RESTORER
> > +
> >  /*
> >   * Syscalls for SPARC:
> >   *   - registers are native word size
> > @@ -188,4 +201,34 @@ pid_t sys_fork(void)
> >  }
> >  #define sys_fork sys_fork
> >  
> > +#define __nolibc_stringify_1(x...)     #x
> > +#define __nolibc_stringify(x...)       __stringify_1(x)
> 
> If we need this, IMO it belongs into compiler.h.
> 
> > +
> > +/* The compiler insists on adding a SAVE call to the start of every 
> > function */
> > +#define __nolibc_sa_restorer __nolibc_sa_restorer
> > +void __nolibc_sa_restorer (void);
> > +#ifdef __arch64__
> > +__asm__(                                                        \
> 
> We are avoiding bare toplevel asm calls.
> You could use the same trick as my SuperH _start() function and use
> asm() inside a function.
> 
> https://lore.kernel.org/lkml/[email protected]/
> 
> > +   ".section .text\n"                                      \
> > +   ".align  4 \n"                                          \
> > +   "__nolibc_sa_restorer:\n"                               \
> > +   "nop\n"                                                 \
> > +   "nop\n"                                                 \
> > +   "mov     " __stringify(__NR_rt_sigreturn) ", %g1 \n"    \
> > +   "t       0x6d \n");
> > +#else
> > +__asm__(                                                        \
> > +   ".section .text\n"                                      \
> > +   ".align  4 \n"                                          \
> > +   "__nolibc_sa_restorer:\n"                               \
> > +   "nop\n"                                                 \
> > +   "nop\n"                                                 \
> > +   "mov     " __stringify(__NR_rt_sigreturn) ", %g1 \n"    \
> > +   "t       0x10 \n"                                       \
> 
> Only one line differs. I'd prefer the #ifdef around that.
> 
> > +   );
> > +#endif
> > +
> > +#undef __nolibc_stringify_1(x...)
> > +#undef __nolibc_stringify
> 
> And there is no need to undef it again.
> 
> > +
> >  #endif /* _NOLIBC_ARCH_SPARC_H */
> > diff --git a/tools/include/nolibc/arch-x86_64.h 
> > b/tools/include/nolibc/arch-x86_64.h
> > index 67305e24dbef..9f13a2205876 100644
> 
> <snip>
> 
> > --- a/tools/include/nolibc/signal.h
> > +++ b/tools/include/nolibc/signal.h
> > @@ -14,6 +14,8 @@
> >  #include "arch.h"
> >  #include "types.h"
> >  #include "sys.h"
> > +#include "string.h"
> > +/* signal definitions are included by arch.h */
> >  
> >  /* This one is not marked static as it's needed by libgcc for divide by 
> > zero */
> >  int raise(int signal);
> > @@ -23,4 +25,99 @@ int raise(int signal)
> >     return sys_kill(sys_getpid(), signal);
> >  }
> >  
> > +/*
> > + * sigaction(int signum, const struct sigaction *act, struct sigaction 
> > *oldact)
> > + */
> > +#if defined(_NOLIBC_ARCH_NEEDS_SA_RESTORER) && 
> > !defined(__nolibc_sa_restorer)
> > +static __attribute__((noreturn)) __nolibc_entrypoint __no_stack_protector
> 
> __attribute__((noreturn)) is not always available and should be behind
> __nolibc_has_attribute(). 
> I'm a bit unhappy about reusing the entrypoint machinery, but I guess
> it's necessary.
> 
> > +void __nolibc_sa_restorer(void)
> > +{
> > +   my_syscall0(__NR_rt_sigreturn);
> > +   __nolibc_entrypoint_epilogue();
> > +}
> > +#endif
> > +
> > +static __attribute__((unused))
> > +int sys_rt_sigaction(int signum, const struct sigaction *act, struct 
> > sigaction *oldact)
> > +{
> > +   struct sigaction real_act = *act;
> > +#if defined(SA_RESTORER) && defined(_NOLIBC_ARCH_NEEDS_SA_RESTORER)
> 
> If _NOLIBC_ARCH_NEEDS_SA_RESTORER is set then SA_RESTORER should also be
> present. SA_RESTORER doesn't need to be checked also.
> Are there cases where SA_RESTORER is defined but not needed?
> 
> > +   if (!(real_act.sa_flags & SA_RESTORER)) {
> > +           real_act.sa_flags |= SA_RESTORER;
> > +           real_act.sa_restorer = __nolibc_sa_restorer;
> > +   }
> > +#endif
> > +#ifdef _NOLIBC_ARCH_FORCE_SIG_FLAGS
> > +   real_act.sa_flags |= _NOLIBC_ARCH_FORCE_SIG_FLAGS;
> > +#endif
> > +
> > +#ifndef _NOLIBC_RT_SIGACTION_PASSES_RESTORER
> > +   return my_syscall4(__NR_rt_sigaction, signum, &real_act, oldact,
> > +                      sizeof(act->sa_mask));
> > +#else
> > +   return my_syscall5(__NR_rt_sigaction, signum, &real_act, oldact,
> > +                      __nolibc_sa_restorer, sizeof(act->sa_mask));
> 
> This calling convention is specific to SPARC, so I prefer it to be in
> arch-sparc.h. Alpha also uses 5 arguments for the syscall, but of course
> in a different order... (I do have nearly done patches for alpha
> support).
> 
> Also if a user specified their custom sa_restorer in 'act', that one
> should be used, no?
> 
> > +#endif
> > +}
> > +
> > +static __attribute__((unused))
> > +int sigaction(int signum, const struct sigaction *act, struct sigaction 
> > *oldact)
> > +{
> > +   return __sysret(sys_rt_sigaction(signum, act, oldact));
> > +}
> > +
> > +/*
> > + * int sigemptyset(sigset_t *set)
> > + */
> > +static __attribute__((unused))
> > +int sigemptyset(sigset_t *set)
> > +{
> > +   memset(set, 0, sizeof(*set));
> > +   return 0;
> > +}
> > +
> > +/*
> > + * int sigfillset(sigset_t *set)
> > + */
> > +static __attribute__((unused))
> > +int sigfillset(sigset_t *set)
> > +{
> > +   memset(set, 0xff, sizeof(*set));
> > +   return 0;
> > +}
> > +
> > +/*
> > + * int sigaddset(sigset_t *set, int signum)
> > + */
> > +static __attribute__((unused))
> > +int sigaddset(sigset_t *set, int signum)
> > +{
> > +   set->sig[(signum - 1) / (8 * sizeof(set->sig[0]))] |=
> > +           1UL << ((signum - 1) % (8 * sizeof(set->sig[0])));
> > +   return 0;
> 
> This is documented to return EINVAL for an invalid signum.
> 
> > +}
> > +
> > +/*
> > + * int sigdelset(sigset_t *set, int signum)
> > + */
> > +static __attribute__((unused))
> > +int sigdelset(sigset_t *set, int signum)
> > +{
> > +   set->sig[(signum - 1) / (8 * sizeof(set->sig[0]))] &=
> > +           ~(1UL << ((signum - 1) % (8 * sizeof(set->sig[0]))));
> > +   return 0;
> > +}
> > +
> > +/*
> > + * int sigismember(sigset_t *set, int signum)
> > + */
> > +static __attribute__((unused))
> > +int sigismember(sigset_t *set, int signum)
> > +{
> > +   unsigned long res =
> > +           set->sig[(signum - 1) / (8 * sizeof(set->sig[0]))] &
> > +                   (1UL << ((signum - 1) % (8 * sizeof(set->sig[0]))));
> > +   return !!res;
> > +}
> 
> These are similar to FD_CLR()/FD_SET() etc, no?
> Moving both sets of functions to common inline helpers would be nice.
> 
> > +
> >  #endif /* _NOLIBC_SIGNAL_H */
> 
> <snip>
> 
> > diff --git a/tools/testing/selftests/nolibc/nolibc-test.c 
> > b/tools/testing/selftests/nolibc/nolibc-test.c
> > index dbe13000fb1a..af66b739ea18 100644
> > --- a/tools/testing/selftests/nolibc/nolibc-test.c
> > +++ b/tools/testing/selftests/nolibc/nolibc-test.c
> > @@ -1750,6 +1750,57 @@ static int run_protection(int min 
> > __attribute__((unused)),
> >     }
> >  }
> >  
> > +volatile int signal_check;
> 
> Technically this should be sig_atomic_t.
> Which can be a typedef to int.
> 
> > +void test_sighandler(int signum)
> > +{
> > +   if (signum == SIGUSR1) {
> > +           kill(getpid(), SIGUSR2);
> > +           signal_check = 1;
> > +   } else {
> > +           signal_check++;
> > +   }
> > +}
> > +
> > +int run_signal(int min, int max)
> > +{
> > +   struct sigaction sa = {
> > +           .sa_flags = 0,
> > +           .sa_handler = test_sighandler,
> > +   };
> > +   int llen; /* line length */
> > +   int ret = 0;
> > +   int res;
> > +
> > +   (void)min;
> > +   (void)max;
> > +
> > +   signal_check = 0;
> > +
> > +   sigemptyset(&sa.sa_mask);
> > +   sigaddset(&sa.sa_mask, SIGUSR2);
> 
> It would be good to do some assertions after sigemptyset() and
> sigaddset() to validate the bitfiddling.
> 
> > +
> > +   res = sigaction(SIGUSR1, &sa, NULL);
> > +   llen = printf("register SIGUSR1: %d", res);
> > +   EXPECT_EQ(1, 0, res);
> > +   res = sigaction(SIGUSR2, &sa, NULL);
> > +   llen = printf("register SIGUSR2: %d", res);
> > +   EXPECT_EQ(1, 0, res);
> 
> Here it would be nice to validate the old action.
> 
> > +
> > +   /* Trigger the first signal. */
> > +   kill(getpid(), SIGUSR1);
> > +
> > +   /* If signal_check is 1 or higher, then signal emission worked */
> > +   llen = printf("signal emission: 1 <= signal_check");
> > +   EXPECT_GE(1, signal_check, 1);
> > +
> > +   /* If it is 2, then signal masking worked */
> > +   llen = printf("signal masking: 2 == signal_check");
> > +   EXPECT_EQ(1, signal_check, 2);
> > +
> > +   return ret;
> > +}
> > +
> >  /* prepare what needs to be prepared for pid 1 (stdio, /dev, /proc, etc) */
> >  int prepare(void)
> >  {
> > @@ -1815,6 +1866,7 @@ static const struct test test_names[] = {
> >     { .name = "stdlib",     .func = run_stdlib     },
> >     { .name = "printf",     .func = run_printf     },
> >     { .name = "protection", .func = run_protection },
> > +   { .name = "signal",     .func = run_signal },
> 
> These 'struct test's are really more test suites.
> This testcase can be part of the syscall suite.
> 
> >     { 0 }
> >  };
> >  
> > -- 
> > 2.50.0
> > 
> 


Reply via email to