[PATCH v12 15/17] nptl: Move the rseq area to the 'extra TLS' block

Michael Jeanson mjeanson@efficios.com
Fri Oct 11 18:01:47 GMT 2024


On 2024-09-14 13:51, Florian Weimer wrote:
> * Michael Jeanson:
> 
>> diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
>> index 1d3665d5ed..9b49ee7121 100644
>> --- a/nptl/pthread_create.c
>> +++ b/nptl/pthread_create.c
>> @@ -691,7 +691,7 @@ __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
>>  
>>    /* Inherit rseq registration state.  Without seccomp filters, rseq
>>       registration will either always fail or always succeed.  */
>> -  if ((int) THREAD_GETMEM_VOLATILE (self, rseq_area.cpu_id) >= 0)
>> +  if ((int) RSEQ_GETMEM_VOLATILE (rseq_get_area(), cpu_id) >= 0)
>>      pd->flags |= ATTR_FLAG_DO_RSEQ;
> 
> Style nit: space in rseq_get_area ()

Ack.

> 
> 
>> diff --git a/sysdeps/nptl/dl-tls_init_tp.c b/sysdeps/nptl/dl-tls_init_tp.c
>> index b2abb2762d..46dc666cbc 100644
>> --- a/sysdeps/nptl/dl-tls_init_tp.c
>> +++ b/sysdeps/nptl/dl-tls_init_tp.c
>> @@ -101,19 +101,17 @@ __tls_init_tp (void)
>>    }
> 
>> +    if (!rseq_register_current_thread (pd, do_rseq))
>> +      {
>> +        _rseq_size = 0;
>> +      }
> 
> Maybe drop the extra braces.

Ack.

> 
>> diff --git a/sysdeps/unix/sysv/linux/rseq-internal.h b/sysdeps/unix/sysv/linux/rseq-internal.h
>> index 2aae447b60..d26d69fd6a 100644
>> --- a/sysdeps/unix/sysv/linux/rseq-internal.h
>> +++ b/sysdeps/unix/sysv/linux/rseq-internal.h
>> @@ -24,6 +24,26 @@
> 
>> +/* rseq area registered with the kernel.  Use a custom definition here to
>> +   isolate from the system provided header which could lack some fields of the
>> +   Extended ABI.
>> +
>> +   Access to fields of the Extended ABI beyond the 20 bytes of the original ABI
>> +   (after 'flags') must be gated by a check of the feature size.  */
>> +struct rseq_area
>> +{
>> +  /* Original ABI.  */
>> +  uint32_t cpu_id_start;
>> +  uint32_t cpu_id;
>> +  uint64_t rseq_cs;
>> +  uint32_t flags;
>> +  /* Extended ABI.  */
>> +  uint32_t node_id;
>> +  uint32_t mm_cid;
>> +};
> 
> (Note: UAPI uses struct rseq, so no conflict here.)
> 
> It's architecture-dependent whether this struct has 4 byte padding at
> the end.  If this struct is only used to get the field offsets and
> sizes, please add a comment to this effect, and maybe a flexible array
> member at the end to discourage direct object allocations.

Ack, I'll add both.

> 
>> +/* Returns a pointer to the current thread rseq area.  */
>> +static inline struct rseq_area *
>> +rseq_get_area(void)
>> +{
>> +#if IS_IN (rtld)
>> +  /* Use the hidden symbol in ld.so.  */
>> +  return (struct rseq_area *) ((char *) __thread_pointer() + _rseq_offset);
>> +#else
>> +  return (struct rseq_area *) ((char *) __thread_pointer() + __rseq_offset);
>> +#endif
>> +}
> 
> I wonder if this should go into the patch that adds RSEQ_GETMEM_VOLATILE
> etc. because it's required by the generic macros.  And maybe name it
> RSEQ_SELF for alignment with THREAD_SELF (but it can remain an inline
> function).

Ack.

> 
> The _rseq_offset/__rseq_offset gate here is ugly.  I believe we can
> avoid it for read access if we also add _GI___rseq_size as an alias in
> the .S file and add rtld_hidden_proto to the declaration of __rseq_size.
> Then in ld.so, we automatically use a hidden symbol for access with
> __rseq_size, and we won't need the #ifdefs.  The _rseq_size alias will
> have to remain because it's not defined as const and used during
> initialization.

Ack.

> 
>> +      /* The feature size can be smaller than the minimum rseq area size of 32
>> +	 bytes accepted by the syscall, if this is the case, bump the size of
>> +	 the registration to the minimum. The 'extra TLS' block is always at
>> +	 least 32 bytes. */
>>        if (size < RSEQ_AREA_SIZE_INITIAL)
>> -        /* The initial implementation used only 20 bytes out of 32,
>> -           but still expected size 32.  */
>>          size = RSEQ_AREA_SIZE_INITIAL;
>> -      int ret = INTERNAL_SYSCALL_CALL (rseq, &self->rseq_area,
>> -                                       size, 0, RSEQ_SIG);
>> +
>> +      /* The kernel expects 'rseq_area->rseq_cs == NULL' on registration, zero
>> +         the whole rseq area.  */
>> +      memset(rseq_get_area(), 0, size);
>> +
>> +      int ret = INTERNAL_SYSCALL_CALL (rseq, rseq_get_area(), size, 0,
>> +		      RSEQ_SIG);
>>        if (!INTERNAL_SYSCALL_ERROR_P (ret))
>>          return true;
> 
> Style nits: missing spaces before '(' in function macro/calls.

Ack.

> 
> This added memset may actually fix an existing bug, but it is hard to
> tell.  On some architectures, the generic TLS code in combination with
> the NPTL code zeroes struct pthread, and thus the rseq area embedded in
> it.  But I'm not sure that this is what happens on all architectures.
> Perhaps we should add a test for this that exits are thread with a
> pollutted rseq area.  In any case, the addition of the memset should go
> into a separate patch, so that we can backport it to all releases with
> rseq support.

Ack, I'll split the memset in a separate patch. I wonder if instead of a memset
of the whole area, we should explicitly clear the fields that are required to
be by the syscall?

> 
>> diff --git a/sysdeps/unix/sysv/linux/tst-rseq-disable.c b/sysdeps/unix/sysv/linux/tst-rseq-disable.c
>> index bbc655bec4..b1f4e894f1 100644
>> --- a/sysdeps/unix/sysv/linux/tst-rseq-disable.c
>> +++ b/sysdeps/unix/sysv/linux/tst-rseq-disable.c
>> @@ -26,32 +26,65 @@
> 
>> +static __thread struct rseq local_rseq = {
>> +  .cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED,
>> +};
> 
> Maybe add a comment: Used to test private registration with the rseq
> system call because glibc rseq is disabled.

Ack.

> 
>>  
>>  /* Check that rseq can be registered and has not been taken by glibc.  */
>>  static void
>>  check_rseq_disabled (void)
>>  {
>> -  struct pthread *pd = THREAD_SELF;
>> +  struct rseq *rseq_area = (struct rseq *) ((char *) __thread_pointer () + __rseq_offset);
> 
> Style nit: Line length (also below).

Ack.

> 
>> --- a/sysdeps/unix/sysv/linux/tst-rseq.c
>> +++ b/sysdeps/unix/sysv/linux/tst-rseq.c
>> @@ -19,6 +19,8 @@
>>     not linked against libpthread.  */
>>  
>>  #include <support/check.h>
>> +#include <support/namespace.h>
>> +#include <support/xthread.h>
>>  #include <stdio.h>
>>  #include <sys/rseq.h>
>>  #include <unistd.h>
>> @@ -32,23 +34,66 @@
>>  # include <sys/auxv.h>
>>  # include <thread_pointer.h>
>>  # include <tls.h>
>> +# include <sys/auxv.h>
>>  # include "tst-rseq.h"
>>  
>>  static void
>>  do_rseq_main_test (void)
>>  {
>> -  struct pthread *pd = THREAD_SELF;
>> +  size_t rseq_align = MAX (getauxval (AT_RSEQ_ALIGN), RSEQ_TEST_MIN_ALIGN);
>> +  size_t rseq_feature_size = MAX (getauxval (AT_RSEQ_FEATURE_SIZE), RSEQ_TEST_MIN_FEATURE_SIZE);
>> +  size_t rseq_alloc_size = roundup (MAX (rseq_feature_size, RSEQ_TEST_MIN_SIZE), rseq_align);
> 
> I think this assumes that getauxval returns 0 if the kernel doesn't have
> these in auxv?

Yes this is correct.

> 
>>  #include <support/test-driver.c>
>> diff --git a/sysdeps/unix/sysv/linux/tst-rseq.h b/sysdeps/unix/sysv/linux/tst-rseq.h
>> index dc603327d3..7a2e19b07f 100644
>> --- a/sysdeps/unix/sysv/linux/tst-rseq.h
>> +++ b/sysdeps/unix/sysv/linux/tst-rseq.h
>> @@ -23,11 +23,16 @@
>>  #include <syscall.h>
>>  #include <sys/rseq.h>
>>  #include <tls.h>
>> +#include <rseq-internal.h>
> 
> Note: This obtains the struct rseq_area definition (the glibc variant of
> the UAPI struct rseq).
> 
> Thanks,
> Florian
> 



More information about the Libc-alpha mailing list