diff options
author | Rich Felker <dalias@aerifal.cx> | 2012-05-23 15:45:41 -0400 |
---|---|---|
committer | Rich Felker <dalias@aerifal.cx> | 2012-05-23 15:45:41 -0400 |
commit | 4da268f74b90696563db4f5d9d2b8e1c1351bdc6 (patch) | |
tree | 6248d447902f2a011c70e2b207e600718bed77a4 /src/thread/cancel_impl.c | |
parent | cfd892fde9454e014d9b291a56ce5740d8bc4a78 (diff) | |
download | musl-4da268f74b90696563db4f5d9d2b8e1c1351bdc6.tar.gz musl-4da268f74b90696563db4f5d9d2b8e1c1351bdc6.tar.bz2 musl-4da268f74b90696563db4f5d9d2b8e1c1351bdc6.tar.xz musl-4da268f74b90696563db4f5d9d2b8e1c1351bdc6.zip |
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.
Diffstat (limited to 'src/thread/cancel_impl.c')
-rw-r--r-- | src/thread/cancel_impl.c | 15 |
1 files changed, 4 insertions, 11 deletions
diff --git a/src/thread/cancel_impl.c b/src/thread/cancel_impl.c index 7652a7c9..3bf1e392 100644 --- a/src/thread/cancel_impl.c +++ b/src/thread/cancel_impl.c @@ -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(); |