fix issue with longjmp out of signal handlers and cancellation
authorRich Felker <dalias@aerifal.cx>
Wed, 23 May 2012 19:45:41 +0000 (15:45 -0400)
committerRich Felker <dalias@aerifal.cx>
Wed, 23 May 2012 19:45:41 +0000 (15:45 -0400)
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.

src/thread/arm/syscall_cp.s
src/thread/cancel_impl.c
src/thread/i386/syscall_cp.s
src/thread/x86_64/syscall_cp.s

index 59924fc51c168564a15ffd15403d0f749aae237a..0cc23b1ff76419a923fca169ac41093bda81fe6c 100644 (file)
@@ -3,20 +3,18 @@
 __syscall_cp_asm:
        mov ip,sp
        stmfd sp!,{r4,r5,r6,r7,lr}
-       stmfd sp!,{r0}
-       bl 1f
-1:     mov r4,#(1f-.)
-       add r4,r4,lr
-       str r4,[r0,#4]
-       str sp,[r0]
+.global __cp_begin
+__cp_begin:
+       ld r0,[r0]
+       cmp r0,#0
+       blne __cancel
        mov r7,r1
        mov r0,r2
        mov r1,r3
        ldmfd ip,{r2,r3,r4,r5,r6}
-1:     svc 0
-       ldmfd sp!,{r1}
-       mov r2,#0
-       str r2,[r1]
+       svc 0
+.global __cp_end
+__cp_end:
        ldmfd sp!,{r4,r5,r6,r7,lr}
        tst lr,#1
        moveq pc,lr
index 7652a7c94b405bd5822f3e784937c2bfbb4028a9..3bf1e39285f6b05afe3ff8737f8e72dcd3f24e33 100644 (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)
 {
        pthread_t self;
-       uintptr_t old_sp, old_ip;
        long r;
 
        if (!libc.main_thread || (self = __pthread_self())->canceldisable)
                return __syscall(nr, u, v, w, x, y, z);
 
-       old_sp = self->cp_sp;
-       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;
+       r = __syscall_cp_asm(&self->cancel, nr, u, v, w, x, y, z);
        if (r==-EINTR && nr!=SYS_close && self->cancel && !self->canceldisable)
                __cancel();
        return r;
@@ -42,14 +35,14 @@ static void cancel_handler(int sig, siginfo_t *si, void *ctx)
 {
        pthread_t self = __pthread_self();
        ucontext_t *uc = ctx;
-       uintptr_t sp = ((uintptr_t *)&uc->uc_mcontext)[CANCEL_REG_SP];
-       uintptr_t ip = ((uintptr_t *)&uc->uc_mcontext)[CANCEL_REG_IP];
+       const char *ip = ((char **)&uc->uc_mcontext)[CANCEL_REG_IP];
+       extern const char __cp_begin[1], __cp_end[1];
 
        if (!self->cancel || self->canceldisable) return;
 
        _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;
                pthread_sigmask(SIG_SETMASK, &uc->uc_sigmask, 0);
                __cancel();
index 05e867a146ec90bd76658ca661141c22496ba500..3bf52c1fe67f1a5e332f87eccdf38456b03c8c39 100644 (file)
@@ -2,34 +2,28 @@
 .global __syscall_cp_asm
 .type   __syscall_cp_asm,@function
 __syscall_cp_asm:
+       mov 4(%esp),%ecx
        pushl %ebx
        pushl %esi
        pushl %edi
        pushl %ebp
-       leal 20(%esp),%ebp
-       call 1f
-1:     popl %eax
-       movl (%ebp),%ecx
-       addl $[1f-1b],%eax
-       movl %eax,4(%ecx)
-       movl %esp,(%ecx)
-       movl 8(%ecx),%eax
+.global __cp_begin
+__cp_begin:
+       movl (%ecx),%eax
        testl %eax,%eax
-       jnz 2f
-       movl 4(%ebp),%eax
-       movl 8(%ebp),%ebx
-       movl 12(%ebp),%ecx
-       movl 16(%ebp),%edx
-       movl 20(%ebp),%esi
-       movl 24(%ebp),%edi
-       movl 28(%ebp),%ebp
-1:     int $128
+       jnz __cancel
+       movl 24(%esp),%eax
+       movl 28(%esp),%ebx
+       movl 32(%esp),%ecx
+       movl 36(%esp),%edx
+       movl 40(%esp),%esi
+       movl 44(%esp),%edi
+       movl 48(%esp),%ebp
+       int $128
+.global __cp_end
+__cp_end:
        popl %ebp
        popl %edi
        popl %esi
        popl %ebx
-       xorl %edx,%edx
-       movl 4(%esp),%ecx
-       movl %edx,(%ecx)
        ret
-2:     call __cancel
index b03635475ab912fa932b0320735a6161e7e57ca0..788c53cc756fdf866a48c8e6ed6dea8f7a106e43 100644 (file)
@@ -2,12 +2,12 @@
 .global __syscall_cp_asm
 .type   __syscall_cp_asm,@function
 __syscall_cp_asm:
-       lea 1f(%rip),%rax
-       mov %rax,8(%rdi)
-       mov %rsp,(%rdi)
-       mov 16(%rdi),%eax
+
+.global __cp_begin
+__cp_begin:
+       mov (%rdi),%eax
        test %eax,%eax
-       jnz 2f
+       jnz __cancel
        mov %rdi,%r11
        mov %rsi,%rax
        mov %rdx,%rdi
@@ -17,9 +17,7 @@ __syscall_cp_asm:
        mov 8(%rsp),%r8
        mov 16(%rsp),%r9
        mov %r11,8(%rsp)
-1:     syscall
-       xor %ecx,%ecx
-       mov 8(%rsp),%rdi
-       mov %rcx,(%rdi)
+       syscall
+.global __cp_end
+__cp_end:
        ret
-2:     call __cancel