From: Benjamin Berg benjamin.berg@intel.com
Add support for sigaction() and implement the normal sa_mask helpers.
On many architectures, linux/signal.h pulls in compatibility definitions for the old sigaction syscall instead of rt_sigaction. However, the kernel can be compiled without support for this compatibility syscall and it also results in sa_mask to be too small for realtime signals.
To work around this, the includes are handled separately for each architecture. This way either linux/signal.h or the asm-generic headers can be used to get the correct definition for the rt_sigaction syscall including sigset_t.
Signed-off-by: Benjamin Berg benjamin.berg@intel.com
---
v1: - Update architecture support (adding sh) - Move sparc sys_rt_sigaction logic into its header - Add sig_atomic_t - Use new BITSET_* macros - Move test into syscall suite - Various other small changes
Signed-off-by: Benjamin Berg benjamin.berg@intel.com --- tools/include/nolibc/arch-arm.h | 7 ++ tools/include/nolibc/arch-arm64.h | 3 + tools/include/nolibc/arch-loongarch.h | 3 + tools/include/nolibc/arch-m68k.h | 10 ++ tools/include/nolibc/arch-mips.h | 3 + tools/include/nolibc/arch-powerpc.h | 8 ++ tools/include/nolibc/arch-riscv.h | 3 + tools/include/nolibc/arch-s390.h | 8 +- tools/include/nolibc/arch-sh.h | 5 + tools/include/nolibc/arch-sparc.h | 47 +++++++++ tools/include/nolibc/arch-x86.h | 13 +++ tools/include/nolibc/signal.h | 103 +++++++++++++++++++ tools/include/nolibc/sys.h | 2 +- tools/include/nolibc/time.h | 3 +- tools/testing/selftests/nolibc/nolibc-test.c | 67 ++++++++++++ 15 files changed, 281 insertions(+), 4 deletions(-)
diff --git a/tools/include/nolibc/arch-arm.h b/tools/include/nolibc/arch-arm.h index 1f66e7e5a444..1faf6c2dbeb8 100644 --- a/tools/include/nolibc/arch-arm.h +++ b/tools/include/nolibc/arch-arm.h @@ -10,6 +10,13 @@ #include "compiler.h" #include "crt.h"
+/* Needed to get the correct struct sigaction definition */ +#define SA_RESTORER 0x04000000 + +/* Avoid linux/signal.h, it has an incorrect _NSIG and sigset_t */ +#include <asm-generic/signal.h> +#include <asm-generic/siginfo.h> + /* Syscalls for ARM in ARM or Thumb modes : * - registers are 32-bit * - stack is 8-byte aligned diff --git a/tools/include/nolibc/arch-arm64.h b/tools/include/nolibc/arch-arm64.h index 02a3f74c8ec8..ad14fc0ae5cb 100644 --- a/tools/include/nolibc/arch-arm64.h +++ b/tools/include/nolibc/arch-arm64.h @@ -10,6 +10,9 @@ #include "compiler.h" #include "crt.h"
+/* Architecture has a usable linux/signal.h */ +#include <linux/signal.h> + /* Syscalls for ARM64 : * - registers are 64-bit * - stack is 16-byte aligned diff --git a/tools/include/nolibc/arch-loongarch.h b/tools/include/nolibc/arch-loongarch.h index 5511705303ea..68d60d04ef59 100644 --- a/tools/include/nolibc/arch-loongarch.h +++ b/tools/include/nolibc/arch-loongarch.h @@ -10,6 +10,9 @@ #include "compiler.h" #include "crt.h"
+/* Architecture has a usable linux/signal.h */ +#include <linux/signal.h> + /* Syscalls for LoongArch : * - stack is 16-byte aligned * - syscall number is passed in a7 diff --git a/tools/include/nolibc/arch-m68k.h b/tools/include/nolibc/arch-m68k.h index 6dac1845f298..981b4cc55a69 100644 --- a/tools/include/nolibc/arch-m68k.h +++ b/tools/include/nolibc/arch-m68k.h @@ -13,6 +13,16 @@ #include "compiler.h" #include "crt.h"
+/* + * Needed to get the correct struct sigaction definition. m68k does not use + * sa_restorer, but it is included in the structure. + */ +#define SA_RESTORER 0x04000000 + +/* Avoid linux/signal.h, it has an incorrect _NSIG and sigset_t */ +#include <asm-generic/signal.h> +#include <asm-generic/siginfo.h> + #define _NOLIBC_SYSCALL_CLOBBERLIST "memory"
#define my_syscall0(num) \ diff --git a/tools/include/nolibc/arch-mips.h b/tools/include/nolibc/arch-mips.h index 0cbac63b249a..fb2f503f151f 100644 --- a/tools/include/nolibc/arch-mips.h +++ b/tools/include/nolibc/arch-mips.h @@ -14,6 +14,9 @@ #error Unsupported MIPS ABI #endif
+/* Architecture has a usable linux/signal.h */ +#include <linux/signal.h> + /* Syscalls for MIPS ABI O32 : * - WARNING! there's always a delayed slot! * - WARNING again, the syntax is different, registers take a '$' and numbers diff --git a/tools/include/nolibc/arch-powerpc.h b/tools/include/nolibc/arch-powerpc.h index 204564bbcd32..c846a7ddcf3c 100644 --- a/tools/include/nolibc/arch-powerpc.h +++ b/tools/include/nolibc/arch-powerpc.h @@ -10,6 +10,14 @@ #include "compiler.h" #include "crt.h"
+/* Needed to get the correct struct sigaction definition */ +#define SA_RESTORER 0x04000000 +#define _NOLIBC_ARCH_NEEDS_SA_RESTORER + +/* Avoid linux/signal.h, it has an incorrect _NSIG and sigset_t */ +#include <asm-generic/signal.h> +#include <asm-generic/siginfo.h> + /* Syscalls for PowerPC : * - stack is 16-byte aligned * - syscall number is passed in r0 diff --git a/tools/include/nolibc/arch-riscv.h b/tools/include/nolibc/arch-riscv.h index 885383a86c38..709e6a262d9a 100644 --- a/tools/include/nolibc/arch-riscv.h +++ b/tools/include/nolibc/arch-riscv.h @@ -10,6 +10,9 @@ #include "compiler.h" #include "crt.h"
+/* Architecture has a usable linux/signal.h */ +#include <linux/signal.h> + /* Syscalls for RISCV : * - stack is 16-byte aligned * - syscall number is passed in a7 diff --git a/tools/include/nolibc/arch-s390.h b/tools/include/nolibc/arch-s390.h index df4c3cc713ac..0dccb6d1ad64 100644 --- a/tools/include/nolibc/arch-s390.h +++ b/tools/include/nolibc/arch-s390.h @@ -5,13 +5,19 @@
#ifndef _NOLIBC_ARCH_S390_H #define _NOLIBC_ARCH_S390_H -#include <linux/signal.h> #include <linux/unistd.h>
#include "compiler.h" #include "crt.h" #include "std.h"
+/* Needed to get the correct struct sigaction definition */ +#define SA_RESTORER 0x04000000 + +/* Avoid linux/signal.h, it has an incorrect _NSIG and sigset_t */ +#include <asm-generic/signal.h> +#include <asm-generic/siginfo.h> + /* Syscalls for s390: * - registers are 64-bit * - syscall number is passed in r1 diff --git a/tools/include/nolibc/arch-sh.h b/tools/include/nolibc/arch-sh.h index a96b8914607e..3378afc78e26 100644 --- a/tools/include/nolibc/arch-sh.h +++ b/tools/include/nolibc/arch-sh.h @@ -7,9 +7,14 @@ #ifndef _NOLIBC_ARCH_SH_H #define _NOLIBC_ARCH_SH_H
+#include <linux/unistd.h> + #include "compiler.h" #include "crt.h"
+/* Architecture has a usable linux/signal.h */ +#include <linux/signal.h> + /* * Syscalls for SuperH: * - registers are 32bit wide diff --git a/tools/include/nolibc/arch-sparc.h b/tools/include/nolibc/arch-sparc.h index ca420d843e25..c9574e7f795a 100644 --- a/tools/include/nolibc/arch-sparc.h +++ b/tools/include/nolibc/arch-sparc.h @@ -12,6 +12,10 @@ #include "compiler.h" #include "crt.h"
+/* The includes are sane, if one sets __WANT_POSIX1B_SIGNALS__ */ +#define __WANT_POSIX1B_SIGNALS__ +#include <linux/signal.h> + /* * Syscalls for SPARC: * - registers are native word size @@ -204,4 +208,47 @@ pid_t sys_vfork(void) } #define sys_vfork sys_vfork
+#define __nolibc_sa_restorer __nolibc_sa_restorer +void __nolibc_sa_restorer(void); +void __nolibc_sa_restorer_wrapper(void); +void __attribute__((weak,noreturn)) __nolibc_entrypoint __no_stack_protector +__nolibc_sa_restorer_wrapper(void) +{ + /* The C function will have a prologue corrupting "sp" */ + __asm__ volatile ( + ".section .text\n" + ".align 4\n" + ".type __nolibc_sa_restorer, @function\n" + "__nolibc_sa_restorer:\n" + "nop\n" + "nop\n" + "mov %0, %%g1 \n" +#ifdef __arch64__ + "t 0x6d\n" +#else + "t 0x10\n" +#endif + ".size __nolibc_sa_restorer, .-__nolibc_sa_restorer\n" + :: "n"(__NR_rt_sigreturn) + ); + __nolibc_entrypoint_epilogue(); +} + +/* + * sparc has ODD_RT_SIGACTION, we need to pass the restorer as an argument + * to rt_sigaction. + */ +#define sys_rt_sigaction sys_rt_sigaction +static __attribute__((unused)) +int sys_rt_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) +{ + struct sigaction real_act = *act; + + /* Otherwise we would need to use sigreturn instead of rt_sigreturn */ + real_act.sa_flags |= SA_SIGINFO; + + return my_syscall5(__NR_rt_sigaction, signum, &real_act, oldact, + __nolibc_sa_restorer, sizeof(act->sa_mask)); +} + #endif /* _NOLIBC_ARCH_SPARC_H */ diff --git a/tools/include/nolibc/arch-x86.h b/tools/include/nolibc/arch-x86.h index d3efc0c3b8ad..1fe75203d834 100644 --- a/tools/include/nolibc/arch-x86.h +++ b/tools/include/nolibc/arch-x86.h @@ -10,8 +10,21 @@ #include "compiler.h" #include "crt.h"
+/* Needed to get the correct struct sigaction definition */ +#define SA_RESTORER 0x04000000 + +/* Restorer must be set on x86 for both 32 and 64 bit */ +#define _NOLIBC_ARCH_NEEDS_SA_RESTORER + +/* Avoid linux/signal.h, it has an incorrect _NSIG and sigset_t */ +#include <asm-generic/signal.h> +#include <asm-generic/siginfo.h> + #if !defined(__x86_64__)
+/* On i386 we need to set SA_SIGINFO to use rt_sigreturn */ +#define _NOLIBC_ARCH_FORCE_SIG_FLAGS SA_SIGINFO + /* Syscalls for i386 : * - mostly similar to x86_64 * - registers are 32-bit diff --git a/tools/include/nolibc/signal.h b/tools/include/nolibc/signal.h index ac13e53ac31d..cbba57012767 100644 --- a/tools/include/nolibc/signal.h +++ b/tools/include/nolibc/signal.h @@ -14,6 +14,14 @@ #include "arch.h" #include "types.h" #include "sys.h" +#include "string.h" +/* other signal definitions are included by arch.h */ + +/* The kernel headers do not provide a sig_atomic_t definition */ +#ifndef __sig_atomic_t_defined +#define __sig_atomic_t_defined 1 +typedef int sig_atomic_t; +#endif
/* This one is not marked static as it's needed by libgcc for divide by zero */ int raise(int signal); @@ -23,4 +31,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 __no_stack_protector +void __nolibc_sa_restorer(void) +{ + my_syscall0(__NR_rt_sigreturn); +} +#endif + +#ifndef sys_rt_sigaction +static __attribute__((unused)) +int sys_rt_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) +{ + struct sigaction real_act = *act; +#if defined(_NOLIBC_ARCH_NEEDS_SA_RESTORER) + 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 + + return my_syscall4(__NR_rt_sigaction, signum, &real_act, oldact, + sizeof(act->sa_mask)); +} +#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) +{ + BITSET_ZERO(set->sig); + 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) +{ + if (signum < 1 || signum > _NSIG) + return __sysret(-EINVAL); + + BITSET_SET(signum - 1, set->sig); + return 0; +} + +/* + * int sigdelset(sigset_t *set, int signum) + */ +static __attribute__((unused)) +int sigdelset(sigset_t *set, int signum) +{ + if (signum < 1 || signum > _NSIG) + return __sysret(-EINVAL); + + BITSET_CLR(signum - 1, set->sig); + return 0; +} + +/* + * int sigismember(sigset_t *set, int signum) + */ +static __attribute__((unused)) +int sigismember(sigset_t *set, int signum) +{ + if (signum < 1 || signum > _NSIG) + return __sysret(-EINVAL); + + return BITSET_ISSET(signum - 1, set->sig); +} + #endif /* _NOLIBC_SIGNAL_H */ diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h index 295e71d34aba..73b935576561 100644 --- a/tools/include/nolibc/sys.h +++ b/tools/include/nolibc/sys.h @@ -14,7 +14,6 @@
/* system includes */ #include <linux/unistd.h> -#include <linux/signal.h> /* for SIGCHLD */ #include <linux/termios.h> #include <linux/mman.h> #include <linux/fs.h> @@ -24,6 +23,7 @@ #include <linux/fcntl.h> /* for O_* and AT_* */ #include <linux/sched.h> /* for clone_args */ #include <linux/stat.h> /* for statx() */ +/* signal definitions are included by arch.h */
#include "errno.h" #include "stdarg.h" diff --git a/tools/include/nolibc/time.h b/tools/include/nolibc/time.h index d02bc44d2643..103574f76515 100644 --- a/tools/include/nolibc/time.h +++ b/tools/include/nolibc/time.h @@ -14,9 +14,8 @@ #include "arch.h" #include "types.h" #include "sys.h" - -#include <linux/signal.h> #include <linux/time.h> +/* signal definitions are included by arch.h */
static __inline__ void __nolibc_timespec_user_to_kernel(const struct timespec *ts, struct __kernel_timespec *kts) diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index a297ee0d6d07..0bed3270a867 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1270,6 +1270,72 @@ int test_namespace(void) return ret; }
+sig_atomic_t signal_check; + +static void sighandler(int signum) +{ + if (signum == SIGUSR1) { + kill(getpid(), SIGUSR2); + signal_check = 1; + } else { + signal_check++; + } +} + +int test_signals(int test_idx) +{ + struct sigaction sa = { + .sa_flags = 0, + .sa_handler = sighandler, + }; + struct sigaction sa_old = { + /* Anything other than SIG_DFL */ + .sa_handler = sighandler, + }; + int llen; /* line length */ + int ret = 0; + int res; + + signal_check = 0; + + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGUSR2); + + res = sigaction(SIGUSR1, &sa, &sa_old); + llen = printf(" register SIGUSR1: %d", res); + EXPECT_SYSZR(1, res); + if (res) + goto out; + + llen = printf(" sa_old.sa_handler: SIG_DFL (%p)", SIG_DFL); + EXPECT_PTREQ(1, SIG_DFL, sa_old.sa_handler); + if (res) + goto out; + + res = sigaction(SIGUSR2, &sa, NULL); + llen = printf(" register SIGUSR2: %d", res); + EXPECT_SYSZR(1, res); + if (res) + goto out; + + /* 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); + +out: + llen = printf("%d %s", test_idx, "sigaction"); + EXPECT_EQ(1, res, 0); + + return ret; +} + /* Run syscall tests between IDs <min> and <max>. * Return 0 on success, non-zero on failure. */ @@ -1398,6 +1464,7 @@ int run_syscall(int min, int max) CASE_TEST(syscall_noargs); EXPECT_SYSEQ(1, syscall(__NR_getpid), getpid()); break; CASE_TEST(syscall_args); EXPECT_SYSER(1, syscall(__NR_statx, 0, NULL, 0, 0, NULL), -1, EFAULT); break; CASE_TEST(namespace); EXPECT_SYSZR(euid0 && proc, test_namespace()); break; + case __LINE__: ret += test_signals(test); break; case __LINE__: return ret; /* must be last */ /* note: do not set any defaults so as to permit holes above */