Re: [RFC][CFT] signal handling fixes
From: Finn Thain
Date: Tue Aug 10 2021 - 21:42:30 EST
Hi Al,
On Tue, 27 Jul 2021, Al Viro wrote:
> Set a handler of e.g. SIGALRM with sigaction(), with a couple of other
> signals in sa_mask (e.g. SIGUSR1 and SIGUSR2). With raise() on those
> inside the SIGALRM handler - then they will become deliverable on return
> from handler. And have SIGUSR1 and SIGUSR2 handlers print siginfo and
> ucontext contents (have them set with SA_SIGINFO in sa_flags, look at
> the second and third arguments of sighandler).
>
> Use alarm(2) to arrange for SIGALRM and sit in a tight loop - that'll
> give you delivery on return from interrupt. Alternatively,
> raise(SIGALRM) will give you delivery on return from trap. And making
> that a SIGBUS handler instead, mmapping a file, truncating it to 0 and
> dereferencing something in mmapped area will give you delivery on return
> from access error trap. Division by zero (and insertion handler on
> SIGFPE) ought to give you a type 2 exception stack frame (4 bytes of aux
> data, that makes shifted exception frame bugger format and vector fields
> of the original).
>
> FWIW, the third argument of handler points to
> struct ucontext {
> unsigned long uc_flags;
> struct ucontext *uc_link;
> stack_t uc_stack;
> struct mcontext uc_mcontext;
> unsigned long uc_filler[80];
> sigset_t uc_sigmask; /* mask last for extensibility */
> };
> and type/vector is stored in uc_filler[54] (216 bytes into the array),
> with aux data from exception stack frame starting from uc_filler[55].
>
I wrote the attached program to implement those tests. I linked it
statically and ran it under "setarch m68k --addr-no-randomize" on three
systems: Aranym, Qemu and Quadra 630. On each system I tested two builds,
1) stock 5.14.0-rc4 and 2) your "untested.m68k" branch rebased onto same.
Everything appears to work normally. I didn't see differences in ucontext
data between mainline build and patched builds, that is, after omitting
"random" differences that always occur from one test run to the next.
(Despite my attempt to avoid random addresses, repeating any test produced
some "random" values in uc_filler. I didn't try to find out what these
values represent. They appear in both builds.)
Do I need to run the program under gdb or strace to see the effect of your
changes?
BTW, I did see some differences between the Motorola 68040 and the
emulated 68040 CPUs.
On the Motorola CPU, uc_filler[54] is 0x00000078 for the signals delivered
on return from interrupt, but Qemu has 0x00000064 and Aranym has either
0x00000070 or 0x00000114.
Another discrepancy is uc_filler[55..67] for the SIGBUS case:
Motorola:
000000d0 ffffffff ffffffff 00007008 effffc90 ..........p.....
000000e0 05210001 00210001 c0000000 00985fec .!...!........_.
000000f0 0000001e 8006aeae 80000e94 0000001e ................
00000100 00000004 00000000 00008001 574c0080 ............WL..
Aranym:
000000d0 ffffffff ffffffff 00007008 c0000000 ..........p.....
000000e0 05210000 00000000 c0000000 c0000000 .!..............
000000f0 8000817c 00000000 00000000 00000000 ...|............
00000100 00000000 00000000 00000000 00000000 ................
Qemu:
000000d0 ffffffff ffffffff 00007008 c0000000 ..........p.....
000000e0 05210000 00000000 c0000000 c0000000 .!..............
000000f0 00000000 00000000 00000000 00000000 ................
00000100 00000000 00000000 00000000 00000000 ................
The other signals don't show discrepancies in uc_filler across CPU types
(that is, after omitting "random" values).
I wonder whether the deviation in emulator behaviour could have
consequences. E.g. I have heard a bug report relating to gdb under
qemu-system-m68k. Perhaps there's a connection.Attachment:
nohup.out-5.14.0-rc4-multi-00003-g420aec9e726e-aranym
Description: Binary data
Attachment:
nohup.out-5.14.0-rc4-multi-00003-g420aec9e726e-q630
Description: Binary data
Attachment:
nohup.out-5.14.0-rc4-multi-00003-g420aec9e726e-qemu
Description: Binary data
Attachment:
nohup.out-5.14.0-rc4-multi-aranym
Description: Binary data
Attachment:
nohup.out-5.14.0-rc4-multi-q630
Description: Binary data
Attachment:
nohup.out-5.14.0-rc4-multi-qemu
Description: Binary data
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ucontext.h>
#include <unistd.h>
static int dnprintf(int fd, const char *fmt, ...)
{
va_list ap;
#define BUFSIZE 1024
static char buf[BUFSIZE];
va_start(ap, fmt);
vsnprintf(buf, BUFSIZE, fmt, ap);
va_end(ap);
return write(fd, buf, strnlen(buf, BUFSIZE));
}
static void write_hex(int fd, void *p, unsigned int n)
{
while (n--) {
unsigned int i = *(unsigned char *)p++;
dnprintf(fd, "%02x", i & 0xff);
}
}
static volatile sig_atomic_t done;
static void handler(int sig, siginfo_t *info, void *ucontext)
{
ucontext_t *uc = ucontext;
dnprintf(2, "%s: si_signo %2d, si_code %3d, si_addr 0x%08x, ",
__func__, info->si_signo, info->si_code, info->si_addr);
dnprintf(2, "uc_mcontext ");
write_hex(2, &uc->uc_mcontext, sizeof(uc->uc_mcontext));
dnprintf(2, ", __glibc_reserved1 ");
write_hex(2, &uc->__glibc_reserved1, sizeof(uc->__glibc_reserved1));
dnprintf(2, "\n");
switch (sig) {
case SIGALRM:
// Arrange for signal delivery on return from handler
dnprintf(2, "%s: raise(SIGUSR1), raise(SIGUSR2)\n", __func__);
raise(SIGUSR1);
raise(SIGUSR2);
break;
case SIGUSR1:
case SIGUSR2:
// Nothing to do
break;
case SIGFPE:
// Fix up divisor
uc->uc_mcontext.gregs[2] = 1; // REG_D2
break;
case SIGBUS:
// Give up
raise(SIGABRT);
}
done = 1;
}
int main(void)
{
sigset_t mask;
sigfillset(&mask);
struct sigaction sa = {
.sa_sigaction = handler,
.sa_mask = mask,
.sa_flags = SA_SIGINFO,
};
sigaction(SIGALRM, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
sigaction(SIGFPE, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
// Arrange for signal delivery on return from trap
dnprintf(2, "%s: raise(SIGALRM)\n", __func__);
raise(SIGALRM);
// Signal delivery on return from interrupt
dnprintf(2, "%s: alarm(2)\n", __func__);
alarm(2);
done = 0;
while (!done)
continue;
// Signal delivery on return from exception
dnprintf(2, "%s: divide by zero\n", __func__);
asm("clrl %%d2 \n\
divsl %%d2,%%d1 \n\
" : : : "d1", "d2");
// Signal delivery on return from fault
char pattern[] = "/tmp/sigXXXXXX";
int fd = mkstemp(pattern);
if (fd == -1)
return 1;
unlink(pattern);
volatile char *addr = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0);
if (addr == (char *)-1)
return 2;
dnprintf(2, "%s: read pointer %p\n", __func__, addr);
*addr;
}