summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Guenther <guenther@cvs.openbsd.org>2013-10-23 05:59:47 +0000
committerPhilip Guenther <guenther@cvs.openbsd.org>2013-10-23 05:59:47 +0000
commite467ead6f36cf9cd3ff96b71f7967e04f5c53f56 (patch)
treea57c7bded014adc6ec3bdb2ac01dff5dff3f410d
parent687b6919359386ad3f08d34a75a870563d409a8f (diff)
In pthread_kill()/pthread_cancel(), hold the target thread's flags
lock across the kill, so that it can't exit (and its tid be reused!) before the kill() discussed with tedu@ marc@
-rw-r--r--lib/librthread/rthread.c47
1 files changed, 42 insertions, 5 deletions
diff --git a/lib/librthread/rthread.c b/lib/librthread/rthread.c
index 6291d160ce2..76c02a68a4f 100644
--- a/lib/librthread/rthread.c
+++ b/lib/librthread/rthread.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rthread.c,v 1.73 2013/07/30 15:31:01 guenther Exp $ */
+/* $OpenBSD: rthread.c,v 1.74 2013/10/23 05:59:46 guenther Exp $ */
/*
* Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org>
* All Rights Reserved.
@@ -461,7 +461,27 @@ fail1:
int
pthread_kill(pthread_t thread, int sig)
{
- return (kill(thread->tid, sig) == 0 ? 0 : errno);
+ pid_t tid;
+ int ret;
+
+ /* killing myself? do it without locking */
+ if (thread == TCB_THREAD())
+ return (kill(thread->tid, sig) == 0 ? 0 : errno);
+
+ /* block the other thread from exiting */
+ _spinlock(&thread->flags_lock);
+ if (thread->flags & THREAD_DYING)
+ ret = (thread->flags & THREAD_DETACHED) ? ESRCH : 0;
+ else {
+ tid = thread->tid;
+ if (tid == 0) {
+ /* should be impossible without DYING being set */
+ ret = ESRCH;
+ } else
+ ret = kill(tid, sig) == 0 ? 0 : errno;
+ }
+ _spinunlock(&thread->flags_lock);
+ return (ret);
}
int
@@ -473,10 +493,27 @@ pthread_equal(pthread_t t1, pthread_t t2)
int
pthread_cancel(pthread_t thread)
{
+ pid_t tid;
- _rthread_setflag(thread, THREAD_CANCELED);
- if (thread->flags & THREAD_CANCEL_ENABLE)
- kill(thread->tid, SIGTHR);
+ _spinlock(&thread->flags_lock);
+ tid = thread->tid;
+ if ((thread->flags & (THREAD_DYING | THREAD_CANCELED)) == 0 &&
+ tid != 0) {
+ thread->flags |= THREAD_CANCELED;
+
+ if (thread->flags & THREAD_CANCEL_ENABLE) {
+
+ /* canceling myself? release the lock first */
+ if (thread == TCB_THREAD()) {
+ _spinunlock(&thread->flags_lock);
+ kill(tid, SIGTHR);
+ return (0);
+ }
+
+ kill(tid, SIGTHR);
+ }
+ }
+ _spinunlock(&thread->flags_lock);
return (0);
}