avoid UB with self pthread_join()

This commit is contained in:
Michael Davidsaver
2024-06-11 10:38:18 -07:00
committed by Andrew Johnson
parent 012139638d
commit 152d306ad8

View File

@ -687,34 +687,54 @@ static epicsThreadOSD *createImplicit(void)
void epicsThreadMustJoin(epicsThreadId id)
{
void *ret = NULL;
int status;
int prev;
epicsThreadId self;
if(!id) {
if(!id)
return;
} else if(epicsAtomicCmpAndSwapIntT(&id->joinable, 1, 0)!=1) {
if(epicsThreadGetIdSelf()==id) {
errlogPrintf("Warning: %s thread self-join of unjoinable\n", id->name);
prev = epicsAtomicCmpAndSwapIntT(&id->joinable, 1, 0);
self = epicsThreadGetIdSelf();
if(prev==0) {
/* join of unjoinable */
if(self==id) {
errlogPrintf(ERL_WARNING ": %s thread self-join of unjoinable\n", id->name);
return;
} else {
/* try to error nicely, however in all likelihood de-ref of
* 'id' has already caused SIGSEGV as we are racing thread exit,
* which free's 'id'.
*/
cantProceed("Error: %s thread not joinable.\n", id->name);
cantProceed(ERL_ERROR ": %s can't join unjoinable %s\n", self->name, id->name);
}
return;
} else if(prev!=1) { /* also not 0, the only other allowed value. */
cantProceed(ERL_ERROR ": %s joins corrupt thread handle\n", self->name);
}
status = pthread_join(id->tid, &ret);
if(status == EDEADLK) {
/* Thread can't join itself (directly or indirectly)
* so we detach instead.
/* from this point, we are responsible to either detach or join (or leak memory) */
if(self!=id) {
status = pthread_join(id->tid, NULL);
checkStatusOnce(status, "pthread_join");
/* on error, continue and attempt to detach */
} else {
/* Thread self pthread_join() isn't portable.
* We choose to allow, and treat as detach.
*/
status = EDEADLK;
}
if(status) {
status = pthread_detach(id->tid);
checkStatusOnce(status, "pthread_detach");
} else checkStatusOnce(status, "pthread_join");
free_threadInfo(id);
}
free_threadInfo(id); /* release joinable reference */
}
LIBCOM_API void epicsStdCall epicsThreadSuspendSelf(void)