diff --git a/sysdeps/nptl/dl-tls_init_tp.c b/sysdeps/nptl/dl-tls_init_tp.c index c57738e9f3..46478324fc 100644 --- a/sysdeps/nptl/dl-tls_init_tp.c +++ b/sysdeps/nptl/dl-tls_init_tp.c @@ -46,6 +46,8 @@ rtld_mutex_dummy (pthread_mutex_t *lock) const unsigned int __rseq_flags; +size_t _rseq_align attribute_hidden; + void __tls_pre_init_tp (void) { @@ -99,10 +101,14 @@ __tls_init_tp (void) } { - bool do_rseq = true; - do_rseq = TUNABLE_GET (rseq, int, NULL); - if (rseq_register_current_thread (pd, do_rseq)) - _rseq_size = RSEQ_AREA_SIZE_INITIAL_USED; + /* If the registration fails or is disabled by tunable, the public + '__rseq_size' will be set to '0' regardless of the feature size of the + allocated rseq area. An rseq area of at least 32 bytes is always + allocated since application code is allowed to check the status of the + rseq registration by reading the content of the 'cpu_id' field. */ + bool do_rseq = TUNABLE_GET (rseq, int, NULL); + if (!rseq_register_current_thread (pd, do_rseq)) + _rseq_size = 0; #ifdef RSEQ_SIG /* This should be a compile-time constant, but the current diff --git a/sysdeps/unix/sysv/linux/dl-parse_auxv.h b/sysdeps/unix/sysv/linux/dl-parse_auxv.h index 7b4427d0a7..2d4243734c 100644 --- a/sysdeps/unix/sysv/linux/dl-parse_auxv.h +++ b/sysdeps/unix/sysv/linux/dl-parse_auxv.h @@ -21,6 +21,7 @@ #include #include #include +#include typedef ElfW(Addr) dl_parse_auxv_t[AT_MINSIGSTKSZ + 1]; @@ -59,5 +60,17 @@ void _dl_parse_auxv (ElfW(auxv_t) *av, dl_parse_auxv_t auxv_values) GLRO(dl_sysinfo) = auxv_values[AT_SYSINFO]; #endif + /* Get the rseq feature size, with a minimum of RSEQ_AREA_SIZE_INITIAL_USED + (20) for kernels that don't have AT_RSEQ_FEATURE_SIZE. Limit the feature + size to RSEQ_AREA_SIZE_MAX_USED (28) which fits the rseq area in 'struct + pthread' and represents the maximum feature size of currently released + kernels. Since no kernels currently cross the 32 bytes of the original + ABI, the semantics of a feature size of 32 or more are still undetermined. + */ + _rseq_size = MIN (MAX (auxv_values[AT_RSEQ_FEATURE_SIZE], + RSEQ_AREA_SIZE_INITIAL_USED), + RSEQ_AREA_SIZE_MAX_USED); + _rseq_align = MAX (auxv_values[AT_RSEQ_ALIGN], RSEQ_MIN_ALIGN); + DL_PLATFORM_AUXV } diff --git a/sysdeps/unix/sysv/linux/rseq-internal.h b/sysdeps/unix/sysv/linux/rseq-internal.h index a0c244b0cf..6f565581c1 100644 --- a/sysdeps/unix/sysv/linux/rseq-internal.h +++ b/sysdeps/unix/sysv/linux/rseq-internal.h @@ -25,13 +25,31 @@ #include #include -/* 32 is the initially required value for the area size. The - actually used rseq size may be less (20 bytes initially). */ +/* Minimum size of the rseq area allocation required by the syscall. The + actually used rseq feature size may be less (20 bytes initially). */ #define RSEQ_AREA_SIZE_INITIAL 32 + +/* Minimum used feature size of the rseq area. */ #define RSEQ_AREA_SIZE_INITIAL_USED 20 -/* The variables are in .data.relro but are not yet write-protected. */ +/* Maximum currently used feature size of the rseq area. */ +#define RSEQ_AREA_SIZE_MAX_USED 28 + +/* Minimum alignment of the rseq area. */ +#define RSEQ_MIN_ALIGN 32 + +/* Alignment requirement of the rseq area. + Populated from the auxiliary vector with a minimum of '32'. + In .data.relro but not yet write-protected. */ +extern size_t _rseq_align attribute_hidden; + +/* Size of the active features in the rseq area. + Populated from the auxiliary vector with a minimum of '20'. + In .data.relro but not yet write-protected. */ extern unsigned int _rseq_size attribute_hidden; + +/* Offset from the thread pointer to the rseq area. + In .data.relro but not yet write-protected. */ extern ptrdiff_t _rseq_offset attribute_hidden; #ifdef RSEQ_SIG diff --git a/sysdeps/unix/sysv/linux/tst-rseq.c b/sysdeps/unix/sysv/linux/tst-rseq.c index aced4bd66c..b152280eff 100644 --- a/sysdeps/unix/sysv/linux/tst-rseq.c +++ b/sysdeps/unix/sysv/linux/tst-rseq.c @@ -38,13 +38,15 @@ static void do_rseq_main_test (void) { struct pthread *pd = THREAD_SELF; + size_t rseq_feature_size = MIN (MAX (getauxval (AT_RSEQ_FEATURE_SIZE), + RSEQ_AREA_SIZE_INITIAL_USED), + RSEQ_AREA_SIZE_MAX_USED); TEST_VERIFY_EXIT (rseq_thread_registered ()); TEST_COMPARE (__rseq_flags, 0); TEST_VERIFY ((char *) __thread_pointer () + __rseq_offset == (char *) &pd->rseq_area); - /* The current implementation only supports the initial size. */ - TEST_COMPARE (__rseq_size, 20); + TEST_COMPARE (__rseq_size, rseq_feature_size); } static void diff --git a/sysdeps/unix/sysv/linux/tst-rseq.h b/sysdeps/unix/sysv/linux/tst-rseq.h index f216d3f7d7..15f512a983 100644 --- a/sysdeps/unix/sysv/linux/tst-rseq.h +++ b/sysdeps/unix/sysv/linux/tst-rseq.h @@ -23,6 +23,7 @@ #include #include #include +#include static inline bool rseq_thread_registered (void)