fix issue with longjmp out of signal handlers and cancellation

stale state information indicating that a thread was possibly blocked
at a cancellation point could get left behind if longjmp was used to
exit a signal handler that interrupted a cancellation point.

to fix the issue, we throw away the state information entirely and
simply compare the saved instruction pointer to a range of code
addresses in the __syscall_cp_asm function. all the ugly PIC work
(which becomes minimal anyway with this approach) is defered to
cancellation time instead of happening at every syscall, which should
improve performance too.

this commit also fixes cancellation on arm, which was mildly broken
(race condition, not checking cancellation flag once inside the
cancellation point zone). apparently i forgot to implement that. the
new arm code is untested, but appears correct; i'll test and fix it
later if there are problems.
This commit is contained in:
Rich Felker
2012-05-23 15:45:41 -04:00
parent cfd892fde9
commit 4da268f74b
4 changed files with 35 additions and 52 deletions

View File

@ -3,20 +3,18 @@
__syscall_cp_asm: __syscall_cp_asm:
mov ip,sp mov ip,sp
stmfd sp!,{r4,r5,r6,r7,lr} stmfd sp!,{r4,r5,r6,r7,lr}
stmfd sp!,{r0} .global __cp_begin
bl 1f __cp_begin:
1: mov r4,#(1f-.) ld r0,[r0]
add r4,r4,lr cmp r0,#0
str r4,[r0,#4] blne __cancel
str sp,[r0]
mov r7,r1 mov r7,r1
mov r0,r2 mov r0,r2
mov r1,r3 mov r1,r3
ldmfd ip,{r2,r3,r4,r5,r6} ldmfd ip,{r2,r3,r4,r5,r6}
1: svc 0 svc 0
ldmfd sp!,{r1} .global __cp_end
mov r2,#0 __cp_end:
str r2,[r1]
ldmfd sp!,{r4,r5,r6,r7,lr} ldmfd sp!,{r4,r5,r6,r7,lr}
tst lr,#1 tst lr,#1
moveq pc,lr moveq pc,lr

View File

@ -14,19 +14,12 @@ long __syscall_cp_asm(volatile void *, long, long, long, long, long, long, long)
long (__syscall_cp)(long nr, long u, long v, long w, long x, long y, long z) long (__syscall_cp)(long nr, long u, long v, long w, long x, long y, long z)
{ {
pthread_t self; pthread_t self;
uintptr_t old_sp, old_ip;
long r; long r;
if (!libc.main_thread || (self = __pthread_self())->canceldisable) if (!libc.main_thread || (self = __pthread_self())->canceldisable)
return __syscall(nr, u, v, w, x, y, z); return __syscall(nr, u, v, w, x, y, z);
old_sp = self->cp_sp; r = __syscall_cp_asm(&self->cancel, nr, u, v, w, x, y, z);
old_ip = self->cp_ip;
self->cp_sp = 0;
self->cp_ip = 0;
r = __syscall_cp_asm(&self->cp_sp, nr, u, v, w, x, y, z);
self->cp_ip = old_ip;
self->cp_sp = old_sp;
if (r==-EINTR && nr!=SYS_close && self->cancel && !self->canceldisable) if (r==-EINTR && nr!=SYS_close && self->cancel && !self->canceldisable)
__cancel(); __cancel();
return r; return r;
@ -42,14 +35,14 @@ static void cancel_handler(int sig, siginfo_t *si, void *ctx)
{ {
pthread_t self = __pthread_self(); pthread_t self = __pthread_self();
ucontext_t *uc = ctx; ucontext_t *uc = ctx;
uintptr_t sp = ((uintptr_t *)&uc->uc_mcontext)[CANCEL_REG_SP]; const char *ip = ((char **)&uc->uc_mcontext)[CANCEL_REG_IP];
uintptr_t ip = ((uintptr_t *)&uc->uc_mcontext)[CANCEL_REG_IP]; extern const char __cp_begin[1], __cp_end[1];
if (!self->cancel || self->canceldisable) return; if (!self->cancel || self->canceldisable) return;
_sigaddset(&uc->uc_sigmask, SIGCANCEL); _sigaddset(&uc->uc_sigmask, SIGCANCEL);
if (self->cancelasync || sp == self->cp_sp && ip <= self->cp_ip) { if (self->cancelasync || ip >= __cp_begin && ip < __cp_end) {
self->canceldisable = 1; self->canceldisable = 1;
pthread_sigmask(SIG_SETMASK, &uc->uc_sigmask, 0); pthread_sigmask(SIG_SETMASK, &uc->uc_sigmask, 0);
__cancel(); __cancel();

View File

@ -2,34 +2,28 @@
.global __syscall_cp_asm .global __syscall_cp_asm
.type __syscall_cp_asm,@function .type __syscall_cp_asm,@function
__syscall_cp_asm: __syscall_cp_asm:
mov 4(%esp),%ecx
pushl %ebx pushl %ebx
pushl %esi pushl %esi
pushl %edi pushl %edi
pushl %ebp pushl %ebp
leal 20(%esp),%ebp .global __cp_begin
call 1f __cp_begin:
1: popl %eax movl (%ecx),%eax
movl (%ebp),%ecx
addl $[1f-1b],%eax
movl %eax,4(%ecx)
movl %esp,(%ecx)
movl 8(%ecx),%eax
testl %eax,%eax testl %eax,%eax
jnz 2f jnz __cancel
movl 4(%ebp),%eax movl 24(%esp),%eax
movl 8(%ebp),%ebx movl 28(%esp),%ebx
movl 12(%ebp),%ecx movl 32(%esp),%ecx
movl 16(%ebp),%edx movl 36(%esp),%edx
movl 20(%ebp),%esi movl 40(%esp),%esi
movl 24(%ebp),%edi movl 44(%esp),%edi
movl 28(%ebp),%ebp movl 48(%esp),%ebp
1: int $128 int $128
.global __cp_end
__cp_end:
popl %ebp popl %ebp
popl %edi popl %edi
popl %esi popl %esi
popl %ebx popl %ebx
xorl %edx,%edx
movl 4(%esp),%ecx
movl %edx,(%ecx)
ret ret
2: call __cancel

View File

@ -2,12 +2,12 @@
.global __syscall_cp_asm .global __syscall_cp_asm
.type __syscall_cp_asm,@function .type __syscall_cp_asm,@function
__syscall_cp_asm: __syscall_cp_asm:
lea 1f(%rip),%rax
mov %rax,8(%rdi) .global __cp_begin
mov %rsp,(%rdi) __cp_begin:
mov 16(%rdi),%eax mov (%rdi),%eax
test %eax,%eax test %eax,%eax
jnz 2f jnz __cancel
mov %rdi,%r11 mov %rdi,%r11
mov %rsi,%rax mov %rsi,%rax
mov %rdx,%rdi mov %rdx,%rdi
@ -17,9 +17,7 @@ __syscall_cp_asm:
mov 8(%rsp),%r8 mov 8(%rsp),%r8
mov 16(%rsp),%r9 mov 16(%rsp),%r9
mov %r11,8(%rsp) mov %r11,8(%rsp)
1: syscall syscall
xor %ecx,%ecx .global __cp_end
mov 8(%rsp),%rdi __cp_end:
mov %rcx,(%rdi)
ret ret
2: call __cancel