[patch v1] nptl: namespace-safe pthread keys implementation

Florian Weimer fweimer@redhat.com
Fri Jul 3 11:14:19 GMT 2026


* Adhemerval Zanella Netto:

> The old design stored a generation sequence number in both the global
> slot and the per-thread slot (KEY_UNUSED and KEY_USABLE). When a key
> was deleted and its slot reused, per-thread values from the old key
> failed the sequence-number check in pthread_getspecific and
> deallocate_tsd, and were silently ignored.
>
> This new design replaces this with something like zero existing
> per-thread slots at key creation. It is seems somewhat weaker:
>
> * a thread calling pthread_setspecific(K, v) reads the global slot
>   (outside any lock) to validate K, then writes its per-thread
>   slot. Between those two steps, another thread can delete K and a third
>   thread can create a new key that reuses the same slot.  The first
>   thread's write then silently deposits a value for the new key.

But the old scheme has the same problem?  It's already use-after-free on
K if it's used after pthread_key_delete.

> * __nptl_deallocate_tsd reads the global destructor and the per-thread
>   value with no synchronisation between them.  If the key is deleted and
>   reused between those two reads, the wrong destructor may be called
>   with the wrong value.
>
>
> I think either it will need to keep the generation counter or add
> synchronization between pthread_setspecific and rely on delete-time
> zeroing. The extra synchronization adds more synchronization what
> should be fast-path, so I am not sure which one is preferable.

A simple scheme does not immediately come to my mind.

We could perhaps use an rwlock.  I'm not sure if we need to hold a lock
while the destructors are running.  It looks to me glibc is only
responsible for avoiding calling the wrong destructor/value combination,
not that no destructors are called after pthread_key_delete returns.

Thanks,
Florian



More information about the Libc-alpha mailing list