Просмотр исходного кода

sigaction: put sa_restorer behind sa_mask where the kernel has no such field

The rt_sigaction path in libc/signal/sigaction.c hands the userspace
struct sigaction to the kernel unchanged, so both layouts have to agree.
The common bits/sigaction.h carried sa_restorer unconditionally, but the
kernel only has that field where __ARCH_HAS_SA_RESTORER is set -- either
directly in arch/*/include/asm/signal.h (arm, m68k, powerpc, s390, sparc,
x86, xtensa) or via uapi/asm-generic/signal.h when the arch defines
SA_RESTORER (arc, arm64, ia64, nios2, sh).

On the arches that have neither -- riscv, csky, microblaze, or1k, nds32
and kvx -- sa_mask sat one pointer too far into the struct and the kernel
installed whatever was in sa_restorer as the handler's signal mask.
Measured on riscv32: with sa_restorer = 0x12345678, sigprocmask() inside
the handler reports 0x12305679, the value plus SIGHUP, minus the SIGSTOP
bit the kernel strips.

It usually goes unnoticed because a zeroed struct sigaction yields an
empty mask; the handler then blocks nothing, not even its own signal. It
becomes visible when the junk hits a bit that matters: tst-cancel20,
tst-cancel21, tst-cancelx20 and tst-cancelx21 failed on riscv32 at -O2
because bit 31 was set, which blocked SIGCANCEL while a handler ran and
delayed the cancellation past the read() it was meant to interrupt.

Only the prefix up to sa_mask has to match: rt_sigaction copies sizeof of
its own struct in both directions (kernel/signal.c), so everything behind
the mask is ours. Move the field there instead of dropping it, which is
what docs/sigaction.txt asked for when the layout was unified in
885f507317: "If sa_restorer field is present in libc but is missing in
kernel_sigaction, add it at the bottom in order to not mess up
kernel_sigaction layout". That document was removed in dc7ad9738. mips
does it this way already, and so do glibc and musl.

This changes the userspace ABI on those six arches -- sa_mask moves one
word forward, the struct keeps its size -- so libc and applications have
to be rebuilt together.

Tested with the full uclibc-ng-test suite under qemu, libc and tests
rebuilt together: riscv32 770 passed / 0 failed (was 4 failures), or1k
769/0, microblaze 759/0, csky 477/0. nds32 and kvx are analysis only,
there is no qemu target for either.

Signed-off-by: Ramin Moussavi <lordrasmus@gmail.com>
ramin 3 дней назад
Родитель
Сommit
6273c2f86c
1 измененных файлов с 16 добавлено и 1 удалено
  1. 16 1
      libc/sysdeps/linux/common/bits/sigaction.h

+ 16 - 1
libc/sysdeps/linux/common/bits/sigaction.h

@@ -20,8 +20,16 @@
 # error "Never include <bits/sigaction.h> directly; use <signal.h> instead."
 #endif
 
+/* These kernels have no sa_restorer in the struct that rt_sigaction expects
+ * (asm-generic/signal.h without SA_RESTORER).  sigaction() hands ours to
+ * the kernel unchanged, so the field must not precede sa_mask there.  */
+#if defined __riscv || defined __csky__ || defined __microblaze__ \
+ || defined __or1k__ || defined __nds32__ || defined __kvx__
+# define __UCLIBC_NO_KERNEL_SA_RESTORER__ 1
+#endif
+
 /* Structure describing the action to be taken when a signal arrives.
- * In uclibc, it is identical to "new" struct kernel_sigaction
+ * In uclibc, it is identical up to sa_mask to "new" struct kernel_sigaction
  * (one from the Linux 2.1.68 kernel).
  * This minimizes amount of translation in sigaction().
  */
@@ -37,8 +45,15 @@ struct sigaction {
 	__sighandler_t  sa_handler;
 #endif
 	unsigned long   sa_flags;
+#ifdef __UCLIBC_NO_KERNEL_SA_RESTORER__
+	/* Behind the mask: the kernel copies only its own size, so it never
+	   reads or writes this field.  */
+	sigset_t        sa_mask;
+	void            (*sa_restorer)(void);
+#else
 	void            (*sa_restorer)(void);
 	sigset_t        sa_mask;
+#endif
 };
 
 /* Bits in `sa_flags'.  */