Rewrite sigaction() to prevent another race.
A signal handler might call sigaction(). We must block all signals, not only our signal, to prevent a race between us and the next signal handler. Use /* comments */ because cpp might expand macros in ! comments though such expansion is probably harmless. The bridge is now shorter by 2 instructions.
This commit is contained in:
parent
2b09d3756c
commit
103d44c27c
|
@ -1,156 +1,194 @@
|
||||||
#define __NR_sigaction 67
|
#define __NR_sigaction 67
|
||||||
#define SIG_BLOCK 0
|
#define __NR_sigprocmask 126
|
||||||
#define SIG_SETMASK 2
|
#define SIG_SETMASK 2
|
||||||
#define MAXSIG 32
|
|
||||||
|
|
||||||
/* offsets into our stack frame */
|
/* offsets into struct sigaction */
|
||||||
#define mynew 16 /* new sigaction */
|
#define sa_handler 0 /* in union with sa_sigaction */
|
||||||
#define mynset 32 /* new signal set */
|
#define sa_mask 4
|
||||||
#define myoset 36 /* old signal set */
|
#define sa_flags 8
|
||||||
#define mysave 40
|
#define sa_restorer 12
|
||||||
#define mysize 56
|
|
||||||
|
/* offsets from stack pointer */
|
||||||
|
#define mynewact 16 /* struct sigaction */
|
||||||
|
#define myoldact 32
|
||||||
|
#define newmask 64 /* signal set */
|
||||||
|
#define oldmask 68
|
||||||
|
#define oldhandler 72
|
||||||
|
#define myret 76
|
||||||
|
#define savelr 80
|
||||||
|
#define signum 84 /* first argument */
|
||||||
|
#define newact 88
|
||||||
|
#define oldact 92
|
||||||
|
|
||||||
.sect .text; .sect .rodata; .sect .data; .sect .bss
|
.sect .text; .sect .rodata; .sect .data; .sect .bss
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Linux calls signal handlers with arguments in registers, but the
|
* Linux calls signal handlers with arguments in registers, but the
|
||||||
* ACK expects arguments on the stack. This sigaction() uses a
|
* ACK expects arguments on the stack. This sigaction() uses a
|
||||||
* "bridge" to move the arguments.
|
* "bridge" to move the arguments, but
|
||||||
|
*
|
||||||
|
* - If the caller passes a bad pointer, this sigaction() causes
|
||||||
|
* SIGBUS or SIGSEGV instead of setting errno = EFAULT.
|
||||||
|
*
|
||||||
|
* - This sigaction() only works with signals 1 to 31, not with
|
||||||
|
* real-time signals 32 to 64.
|
||||||
|
*
|
||||||
|
* - This sigaction() is not safe for multiple threads.
|
||||||
|
*
|
||||||
|
* int sigaction(int signum, const struct sigaction *newact,
|
||||||
|
* struct sigaction *oldact);
|
||||||
*/
|
*/
|
||||||
.sect .text
|
.sect .text
|
||||||
.define _sigaction
|
.define _sigaction
|
||||||
_sigaction:
|
_sigaction:
|
||||||
mflr r0
|
mflr r0
|
||||||
subi r1, r1, mysize
|
li r3, __NR_sigprocmask
|
||||||
stw r31, mysave+8(r1)
|
stwu r3, -signum(sp) /* keep 0(sp) = __NR_sigprocmask */
|
||||||
stw r30, mysave+4(r1)
|
stw r0, savelr(sp)
|
||||||
stw r29, mysave(r1)
|
|
||||||
stw r0, mysave+12(r1)
|
/* Copy newact to stack (before blocking SIGBUS, SIGSEGV). */
|
||||||
li r3, 0
|
lwz r3, newact(sp)
|
||||||
stw r3, mynset(r1) ! mynset = 0
|
mr. r3, r3
|
||||||
lwz r29, mysize(r1) ! r29 = signal number
|
beq 1f /* skip if newact == NULL */
|
||||||
lwz r30, mysize+4(r1) ! r30 = new action
|
lwz r4, sa_handler(r3)
|
||||||
lwz r31, mysize+8(r1) ! r31 = old action
|
lwz r5, sa_mask(r3)
|
||||||
|
lwz r6, sa_flags(r3)
|
||||||
|
lwz r7, sa_restorer(r3)
|
||||||
|
stw r4, mynewact+sa_handler(sp)
|
||||||
|
stw r5, mynewact+sa_mask(sp)
|
||||||
|
stw r6, mynewact+sa_flags(sp)
|
||||||
|
stw r7, mynewact+sa_restorer(sp)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the new action is non-NULL, the signal number is in
|
* Block all signals to prevent a race. After we set sharray,
|
||||||
* range 1 to MAXSIG, and the new handler is not SIG_DFL 0
|
* we must call the kernel's sigaction before the next signal
|
||||||
* or SIG_IGN 1, then we interpose our bridge.
|
* handler runs. This prevents two problems:
|
||||||
|
*
|
||||||
|
* - The bridge might call the new handler while the kernel
|
||||||
|
* uses the mask and flags of the old handler.
|
||||||
|
*
|
||||||
|
* - The signal handler might call sigaction() and destroy
|
||||||
|
* sharray. We must block all signals because any signal
|
||||||
|
* handler might call sigaction() for our signal.
|
||||||
*/
|
*/
|
||||||
cmpwi cr0, r30, 0
|
1: li r4, SIG_SETMASK
|
||||||
subi r7, r29, 1 ! r7 = index in handlers
|
li r5, -1 /* mask signals 1 to 32 */
|
||||||
cmplwi cr7, r7, MAXSIG ! unsigned comparison
|
stw r5, newmask(sp)
|
||||||
beq cr0, kernel
|
la r5, newmask(sp)
|
||||||
bge cr7, kernel
|
la r6, oldmask(sp)
|
||||||
lwz r3, 0(r30) ! r3 = new handler
|
stw r4, 4(sp) /* kept 0(sp) = __NR_sigprocmask */
|
||||||
clrrwi. r3, r3, 1
|
stw r5, 8(sp)
|
||||||
beq cr0, kernel
|
stw r6, 12(sp)
|
||||||
/*
|
|
||||||
* Block the signal while we build the bridge. Prevents a
|
|
||||||
* race if a signal arrives after we change the bridge but
|
|
||||||
* before we change the action in the kernel.
|
|
||||||
*/
|
|
||||||
li r4, 1
|
|
||||||
slw r4, r4, r7
|
|
||||||
stw r4, mynset(r1) ! mynmask = 1 << (signal - 1)
|
|
||||||
li r3, SIG_BLOCK
|
|
||||||
la r4, mynset(r1)
|
|
||||||
la r5, myoset(r1)
|
|
||||||
stw r3, 0(r1)
|
|
||||||
stw r4, 4(r1)
|
|
||||||
stw r5, 8(r1)
|
|
||||||
bl _sigprocmask
|
|
||||||
/*
|
|
||||||
* Point our bridge to the new signal handler. Then copy the
|
|
||||||
* new sigaction but point it to our bridge.
|
|
||||||
*/
|
|
||||||
lis r6, hi16[handlers]
|
|
||||||
ori r6, r6, lo16[handlers]
|
|
||||||
subi r7, r29, 1
|
|
||||||
slwi r7, r7, 2
|
|
||||||
lwz r3, 0(r30) ! r3 = new handler
|
|
||||||
stwx r3, r6, r7 ! put it in array of handlers
|
|
||||||
lis r3, hi16[bridge]
|
|
||||||
ori r3, r3, lo16[bridge]
|
|
||||||
lwz r4, 4(r30)
|
|
||||||
lwz r5, 8(r30)
|
|
||||||
lwz r6, 12(r30)
|
|
||||||
stw r3, mynew(r1) ! sa_handler or sa_sigaction
|
|
||||||
stw r4, mynew+4(r1) ! sa_mask
|
|
||||||
stw r5, mynew+8(r1) ! sa_flags
|
|
||||||
stw r6, mynew+12(r1) ! sa_restorer
|
|
||||||
la r30, mynew(r1)
|
|
||||||
kernel:
|
|
||||||
li r3, __NR_sigaction
|
|
||||||
stw r3, 0(r1)
|
|
||||||
stw r29, 4(r1)
|
|
||||||
stw r30, 8(r1)
|
|
||||||
stw r31, 12(r1)
|
|
||||||
bl __syscall
|
bl __syscall
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we blocked the signal, then restore the old signal mask.
|
* If the signal number is in range 1 to 31, and the new
|
||||||
|
* handler is not SIG_DFL 0 or SIG_IGN 1, then we interpose
|
||||||
|
* our bridge.
|
||||||
*/
|
*/
|
||||||
lwz r3, mynset(r1)
|
lwz r4, signum(sp) /* keep r4 = signum */
|
||||||
cmpwi cr0, r3, 0
|
addi r5, r4, -1
|
||||||
beq cr0, fixold
|
cmplwi r5, 30
|
||||||
li r3, SIG_SETMASK
|
bgt 2f /* skip if out of range */
|
||||||
la r4, myoset(r1)
|
|
||||||
li r5, 0
|
slwi r5, r5, 2 /* r5 = sharray index */
|
||||||
stw r3, 0(r1)
|
lis r6, ha16[sharray]
|
||||||
stw r4, 4(r1)
|
la r6, lo16[sharray](r6) /* r6 = sharray */
|
||||||
stw r5, 8(r1)
|
lwzx r0, r6, r5
|
||||||
bl _sigprocmask
|
stw r0, oldhandler(sp) /* remember old handler */
|
||||||
/*
|
lwz r0, newact(sp)
|
||||||
* If the old sigaction is non-NULL and points to our bridge,
|
mr. r0, r0
|
||||||
* then point it to the signal handler.
|
beq 2f /* skip if newact == NULL */
|
||||||
*/
|
|
||||||
fixold:
|
lwz r3, mynewact+sa_handler(sp)
|
||||||
cmpwi cr0, r31, 0
|
cmplwi r3, 2 /* r3 = new handler */
|
||||||
beq cr0, leave
|
blt 2f /* skip if SIG_DFL or SIG_IGN */
|
||||||
lis r3, hi16[bridge]
|
|
||||||
ori r3, r3, lo16[bridge]
|
stwx r3, r6, r5 /* put new handler in sharray */
|
||||||
lwz r4, 0(r31)
|
lis r3, ha16[sigbridge]
|
||||||
cmpw cr0, r3, r4
|
la r3, lo16[sigbridge](r3)
|
||||||
bne cr0, leave
|
stw r3, mynewact+sa_handler(sp)
|
||||||
lis r6, hi16[handlers]
|
|
||||||
ori r6, r6, lo16[handlers]
|
/* Call the kernel's sigaction. */
|
||||||
subi r7, r29, 1
|
/* sigaction(signum, &mynewact or NULL, &myoldact or NULL) */
|
||||||
slwi r7, r7, 2
|
2: li r3, __NR_sigaction
|
||||||
lwzx r3, r6, r7 ! get it from array of handlers
|
lwz r0, newact(sp)
|
||||||
stw r3, 0(r31) ! put it in old sigaction
|
mr. r0, r0
|
||||||
leave:
|
beq 3f
|
||||||
lwz r0, mysave+12(r1)
|
la r5, mynewact(sp)
|
||||||
lwz r29, mysave(r1)
|
b 4f
|
||||||
lwz r30, mysave+4(r1)
|
3: li r5, 0
|
||||||
lwz r31, mysave+8(r1)
|
4: lwz r0, oldact(sp)
|
||||||
addi r1, r1, mysize
|
mr. r0, r0
|
||||||
|
beq 5f
|
||||||
|
la r6, myoldact(sp)
|
||||||
|
b 6f
|
||||||
|
5: li r6, 0
|
||||||
|
6: stw r3, 0(sp)
|
||||||
|
stw r4, 4(sp) /* kept r4 = signum */
|
||||||
|
stw r5, 8(sp)
|
||||||
|
stw r6, 12(sp)
|
||||||
|
bl __syscall
|
||||||
|
stw r3, myret(sp)
|
||||||
|
|
||||||
|
/* Unblock signals by restoring old signal mask. */
|
||||||
|
li r3, __NR_sigprocmask
|
||||||
|
li r4, SIG_SETMASK
|
||||||
|
la r5, oldmask(sp)
|
||||||
|
li r6, 0
|
||||||
|
stw r3, 0(sp)
|
||||||
|
stw r4, 4(sp)
|
||||||
|
stw r5, 8(sp)
|
||||||
|
stw r6, 12(sp)
|
||||||
|
bl __syscall
|
||||||
|
|
||||||
|
/* Copy oldact from stack (after unblocking BUS, SEGV). */
|
||||||
|
lwz r3, oldact(sp)
|
||||||
|
mr. r3, r3
|
||||||
|
beq 8f /* skip if oldact == NULL */
|
||||||
|
lwz r4, myoldact+sa_handler(sp)
|
||||||
|
lis r5, ha16[sigbridge]
|
||||||
|
la r5, lo16[sigbridge](r5)
|
||||||
|
cmpw r4, r5
|
||||||
|
bne 7f
|
||||||
|
lwz r4, oldhandler(sp)
|
||||||
|
7: lwz r5, myoldact+sa_mask(sp)
|
||||||
|
lwz r6, myoldact+sa_flags(sp)
|
||||||
|
lwz r7, myoldact+sa_restorer(sp)
|
||||||
|
stw r4, sa_handler(r3)
|
||||||
|
stw r5, sa_mask(r3)
|
||||||
|
stw r6, sa_flags(r3)
|
||||||
|
stw r7, sa_restorer(r3)
|
||||||
|
|
||||||
|
8: lwz r0, savelr(sp)
|
||||||
|
lwz r3, myret(sp)
|
||||||
|
addi sp, sp, signum
|
||||||
mtlr r0
|
mtlr r0
|
||||||
blr ! return from sigaction
|
blr
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Linux calls bridge(signum) or bridge(signum, info, context) with
|
* Linux calls sigbridge(signum) or sigbridge(signum, info, context)
|
||||||
* arguments in registers r3, r4, r5.
|
* with arguments in registers r3, r4, r5.
|
||||||
*/
|
*/
|
||||||
bridge:
|
sigbridge:
|
||||||
mflr r0
|
mflr r0
|
||||||
subi r1, r1, 16
|
stwu r3, -16(sp) /* signal number */
|
||||||
|
stw r4, 4(sp) /* info */
|
||||||
|
stw r5, 8(sp) /* context */
|
||||||
stw r0, 12(r1)
|
stw r0, 12(r1)
|
||||||
stw r3, 0(r1) ! signal number
|
|
||||||
stw r4, 4(r1) ! info
|
|
||||||
stw r5, 8(r1) ! context
|
|
||||||
|
|
||||||
lis r6, hi16[handlers]
|
lis r6, hi16[sharray - 4]
|
||||||
ori r6, r6, lo16[handlers]
|
la r6, lo16[sharray - 4](r6)
|
||||||
subi r7, r3, 1
|
slwi r7, r3, 2
|
||||||
slwi r7, r7, 2
|
|
||||||
lwzx r6, r6, r7
|
lwzx r6, r6, r7
|
||||||
mtctr r6
|
mtctr r6
|
||||||
bctrl ! call our signal handler
|
bctrl /* call our signal handler */
|
||||||
|
|
||||||
lwz r0, 12(r1)
|
lwz r0, 12(sp)
|
||||||
addi r1, r1, 16
|
addi r1, r1, 16
|
||||||
mtlr r0
|
mtlr r0
|
||||||
blr ! return from bridge
|
blr /* sigreturn(2) */
|
||||||
|
|
||||||
.sect .bss
|
.sect .bss
|
||||||
handlers:
|
sharray:
|
||||||
.space 4 * MAXSIG ! array of signal handlers
|
.space 4 * 31 /* handlers for signals 1 to 31 */
|
||||||
|
|
Loading…
Reference in a new issue