[PATCH 3/6] nptl: Do not use pthread set_tid_address as state synchronization (BZ #19951)

Florian Weimer fweimer@redhat.com
Thu Dec 18 14:49:36 GMT 2025


* Adhemerval Zanella:

> The use-after-free described in BZ#19951 is due to the use of two
> different PD fields, 'joinid' and 'cancelhandling', to describe the
> thread state and to synchronise the calls of pthread_join,
> pthread_detach, pthread_exit, and normal thread exit.
>
> Any state change may require checking both fields atomically to handle
> partial state (e.g., pthread_join() with a cancellation handler to
> issue a 'joinstate' field rollback).
>
> This patch uses a different PD member with 4 possible states (JOINABLE,
> DETACHED, EXITING, and EXITED) instead of the pthread 'tid' field, with
> the following logic:
>
>  1. On pthread_create, the initial state is set either to JOINABLE or
>     DETACHED depending on the pthread attribute used.
>
>  2. On pthread_detach, a CAS is issued on the state.  If the CAS fails,
>     the thread is already detached (DETACHED) or being terminated (EXITING).
>     For the former, an EINVAL is returned; for the latter, pthread_detach
>     should be responsible for joining the thread (and for deallocating any
>     internal resources).
>
>  3. In the exit phase of the wrapper function for the thread start routine
>     (reached either if the thread function has returned, pthread_exit has
>     been called, or cancellation handled has been acted upon), we issue a
>     CAS on state to set it to the EXITING mode.
>
>     If the thread is previously in DETACHED mode, the thread is responsible
>     for deallocating any resources; otherwise, the thread must be joined
>     (detached threads cannot deallocate themselves immediately).
>
>  4. The clear_tid_field on 'clone' call is changed to set the new 'state'
>     field on thread exit (EXITED).  This state is only reached at thread
>     termination.
>
>  5. The pthread_join implementation is now simpler: the futex wait is done
>     directly on thread state, and there is no need to reset it in case of
>     timeout since the state is now set either by pthread_detach() or by the
>     kernel on process termination.
>
> The race condition on pthread_detach is avoided with a single atomic
> operation on the PD state: once the mode is set to THREAD_STATE_DETACHED, it
> is up to the thread itself to deallocate its memory (done during the exit
> phase at pthread_create()).
>
> Also, the INVALID_NOT_TERMINATED_TD_P is removed since a negative yid is
> not possible, and the macro is not used anywhere.
>
> This change triggers an invalid C11 thread test: it creates a thread that
> detaches, and after a timeout, the creating thread checks whether the join
> fails.  The issue is that once thrd_join() is called, the thread's lifetime
> is not defined.
>
> Checked on x86_64-linux-gnu, i686-linux-gnu, aarch64-linux-gnu,
> arm-linux-gnueabihf, and powerpc64-linux-gnu.

This version looks okay to me.

Reviewed-by: Florian Weimer <fweimer@redhat.com>

Thanks,
Florian



More information about the Libc-alpha mailing list