Skip to content

Commit 48596a1

Browse files
committed
lib/pthread: Fix create/join race in thread lifecycle
The pthread_join() implementation was done using a condition variable and not k_thread_join(), and that's actually fatal. It's not possible in Zephyr to guarantee a thread has exited in a race-free way without calling either k_thread_abort() or k_thread_join(). This opened a hole where an app could call pthread_join(), which would go to sleep waiting on the condition variable, which would be signaled from the exit path of the thread, and then return to call pthread_create() (and thus k_thread_create()) on the same pthread_t object (and thus the same struct k_thread) BEFORE THE EARLIER THREAD HAD ACTUALLY EXITED. This makes the scheduler blow up, at least on SMP (I wasn't personally able to make it happen on uniprocessor configs, though the race is present always). Rework to call k_thread_join(). There's no other correct way to do this. Fixes zephyrproject-rtos#56163 Signed-off-by: Andy Ross <[email protected]>
1 parent c897766 commit 48596a1

File tree

2 files changed

+7
-15
lines changed

2 files changed

+7
-15
lines changed

lib/posix/posix_internal.h

-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ struct posix_thread {
5454
/* Pthread State */
5555
enum pthread_state state;
5656
pthread_mutex_t state_lock;
57-
pthread_cond_t state_cond;
5857
};
5958

6059
typedef struct pthread_key_obj {

lib/posix/pthread.c

+7-14
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ int pthread_create(pthread_t *newthread, const pthread_attr_t *_attr,
159159
k_spinlock_key_t key;
160160
uint32_t pthread_num;
161161
k_spinlock_key_t cancel_key;
162-
pthread_condattr_t cond_attr;
163162
struct posix_thread *thread;
164163
const struct pthread_attr *attr = (const struct pthread_attr *)_attr;
165164

@@ -207,7 +206,6 @@ int pthread_create(pthread_t *newthread, const pthread_attr_t *_attr,
207206
thread->state = attr->detachstate;
208207
pthread_mutex_unlock(&thread->state_lock);
209208

210-
pthread_cond_init(&thread->state_cond, &cond_attr);
211209
sys_slist_init(&thread->key_list);
212210

213211
*newthread = pthread_num;
@@ -274,7 +272,6 @@ int pthread_cancel(pthread_t pthread)
274272
} else {
275273
thread->retval = PTHREAD_CANCELED;
276274
thread->state = PTHREAD_EXITED;
277-
pthread_cond_broadcast(&thread->state_cond);
278275
}
279276
pthread_mutex_unlock(&thread->state_lock);
280277

@@ -395,7 +392,6 @@ void pthread_exit(void *retval)
395392
if (self->state == PTHREAD_JOINABLE) {
396393
self->state = PTHREAD_EXITED;
397394
self->retval = retval;
398-
pthread_cond_broadcast(&self->state_cond);
399395
} else {
400396
self->state = PTHREAD_TERMINATED;
401397
}
@@ -413,8 +409,6 @@ void pthread_exit(void *retval)
413409
pthread_mutex_unlock(&self->state_lock);
414410
pthread_mutex_destroy(&self->state_lock);
415411

416-
pthread_cond_destroy(&self->state_cond);
417-
418412
k_thread_abort((k_tid_t)self);
419413
}
420414

@@ -436,12 +430,14 @@ int pthread_join(pthread_t thread, void **status)
436430
return ESRCH;
437431
}
438432

439-
pthread_mutex_lock(&pthread->state_lock);
440-
441-
if (pthread->state == PTHREAD_JOINABLE) {
442-
pthread_cond_wait(&pthread->state_cond, &pthread->state_lock);
433+
if (pthread->state == PTHREAD_DETACHED) {
434+
return EINVAL;
443435
}
444436

437+
k_thread_join(&pthread->thread, K_FOREVER);
438+
439+
pthread_mutex_lock(&pthread->state_lock);
440+
445441
if (pthread->state == PTHREAD_EXITED) {
446442
if (status != NULL) {
447443
*status = pthread->retval;
@@ -479,10 +475,7 @@ int pthread_detach(pthread_t thread)
479475
switch (pthread->state) {
480476
case PTHREAD_JOINABLE:
481477
pthread->state = PTHREAD_DETACHED;
482-
/* Broadcast the condition.
483-
* This will make threads waiting to join this thread continue.
484-
*/
485-
pthread_cond_broadcast(&pthread->state_cond);
478+
z_thread_wake_joiners(&pthread->thread);
486479
break;
487480
case PTHREAD_EXITED:
488481
pthread->state = PTHREAD_TERMINATED;

0 commit comments

Comments
 (0)