Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <87zgwo6ph2.fsf@oldenburg.str.redhat.com>
Date: Fri, 21 May 2021 18:06:33 +0200
From: Florian Weimer <fweimer@...hat.com>
To: libc-coord@...ts.openwall.com
Cc: Kostya Serebryany <kcc@...gle.com>
Subject: Thread properties API

In glibc, we have a historic gap when it comes to access to per-thread
data that may contain application data.  Such functionality is required
to support memory leak detectors and conservative garbage collectors
that need to discover pointer values in TLS data.  The sanitizers
currently hard-code glibc implementation details (that are not really
unchanging in practice), and we want to switch to a defined interface
eventually.

We provide some access to per-thread data using libthread_db, but that
functionality is incomplete in several ways and not readily consumable
inside a process.

There is a completely different proposal here:

  <https://sourceware.org/glibc/wiki/ThreadPropertiesAPI>

The interfaces that follow below avoid callback functions that must be
invoked with internal locks held because that is prone to lead to
deadlocks.  They also avoid encoding a specific, unchanging layout for
the static TLS area (which can be extended, but not moved) or internal
pthread_setspecific tables, or whether any such per-thread data
structures are allocated by malloc.

I'd appreciate comments on this proposal.  It's very early and I'm not
entirely convinced yet that it is actually implementable. 8-)

void pthread_retain_np (pthread_t thread);

pthread_retain_np marks THREAD for retention.  A retained thread
identifier remains valid after the thread has exited and (if joinable)
has been joined, but all subsequent operations on the thread ID, except
for pthread_release_np, pthread_getattr_np and pthread_tls_areas_get_np,
will fail.

void pthread_release_np (pthread_t thread);

pthread_release_np undoes the effect of a previous pthread_retain_np
call (which can be implied by pthread_all_threads_np).  Every
pthread_retain_np call must eventually be paired with a
pthread_release_np call, or otherwise there is a resource leak.

Once the number of pthread_release_np calls is equal to the number of
pthread_retain_np calls for a particular thread ID (including such calls
implied by pthread_all_threads_np), the thread ID is again only valid
while the thread is running or joinable.  If the number of calls it is
equal, it is undefined to call pthread_release_np.

size_t pthread_all_threads_np (pthread_t *result, size_t length);

pthread_all_threads_np returns the number of all currently running or
joinable threads in the process.  The identifiers of the first LENGTH
such threads are written to the array starting at RESULT.  For those
thread identifiers, pthread_retrain_np is invoked; this happens in such
a way that the thread is running or joinable at this point.

Applications need to call pthread_release_np on all the thread
identifiers that pthread_all_threads_np has written to RESULT.  This
also applies to the case where pthread_all_threads_np is called in a
loop that grows the RESULT array to the size required to store all
thread IDs in the current process.

Due to the lack of synchronization, an unspecified time can pass between
the termination of a detached thread and the time its thread ID no
longer appears among the thread IDs provided by pthread_all_threads_np.
It is unspecified whether threads that are neither running nor joinable,
but have been retained, appear among those thread IDs.

struct pthread_tls_area_np { const void *start; size_t length; };
size_t pthread_tls_areas_get_np (pthread_t thread,
  struct pthread_tls_area_np *areas, size_t length);
size_t pthread_tls_areas_release_np (pthread_t thread,
  const struct pthread_tls_area_np *areas, size_t count);

pthread_tls_area_get_np returns the number of TLS areas currently
allocated for THREAD.  This number may be zero if THREAD refers to a
thread that is not running.  The location and size of up to LENGTH of
these areas are written to the array starting at AREAS, followed by zero
elements until LENGTH array elements have been written.  An application
can detect that the provided array is too small by check the return
value against LENGTH.

The application may inspect the pointers and memory areas identified by
the array elements (up to the return value of pthread_tls_area_np).  At
this point, it is guaranteed that the memory locations remain valid for
access.  After inspecting the TLS areas, the application must call
pthread_tls_areas_release_np, passing the same THREAD, AREAS and LENGTH
arguments that were used in the pthread_tls_areas_get_np.

For example, if it has been previously determined that a __thread
variable is at address P for a particular THREAD, and if the LENGTH
argument to pthread_tls_area_np is sufficiently large to hold all TLS
areas for THREAD, then P will be contain within one of the TLS areas.
If the implementation supports access to __thread variables from other
threads, it is safe to access *P, subject to the usual constraints
regarding data races, until pthread_tls_areas_release_np is called.
Similarly, pointer values stored by pthread_setspecific will appear in
the TLS areas at unspecified locations, and the values will be current
in the sense that if a pthread_setspecific call for a key happens-before
the pthread_tls_areas_get_np call and the access to the TLS areas
happens-before the next pthread_setspecific call for that key on the
thread, then the pointer value stored by the first pthread_setspecific
call will appear in one of the TLS areas listed by
pthread_tls_areas_get_np.  However, writes to that pointer value may not
be reflected in future pthread_getspecific calls (even with
synchronization).

The distribution of various TLS data structures among the AREAS array is
unspecified.  Some of the areas may be allocated on the heap using
malloc, or part of such heap allocations.  It is unspecified whether
previously allocated TLS areas are returned for a thread that is no
longer running.  If any per-thread data is allocated by the
implementation in such a way that it will be deallocated using free,
pointers to such allocations should appear among the areas returned by
pthread_tls_areas_get_np, so that internal allocations made by the
implementation are not falsely flagged as leaked.

Thanks,
Florian

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.