From: Michael Jeanson Date: Fri, 26 Jul 2024 19:50:54 +0000 (+0000) Subject: Only set 'rseq_size' on first thread registration X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=c67d198627c2c2b68e38b1d75c11ed2e066872c9;p=librseq.git Only set 'rseq_size' on first thread registration To mimic the libc behavior, don't set the feature size in 'rseq_size' until at least one thread has successfully registered. This allows using 'rseq_size' in fast-paths to test for both registration status and available features. The caveat is that on libc either all threads are registered or none are, while with bare librseq it is the responsability of the user to register all threads using rseq. Change-Id: Idc491153de55f9ac38bd1fcad425aee9549fc910 Signed-off-by: Michael Jeanson Signed-off-by: Mathieu Desnoyers --- diff --git a/src/rseq.c b/src/rseq.c index 61f3532..7ee3616 100644 --- a/src/rseq.c +++ b/src/rseq.c @@ -54,7 +54,6 @@ unsigned int rseq_size = -1U; unsigned int rseq_flags; static int rseq_ownership; -static int rseq_reg_success; /* At least one rseq registration has succeded. */ /* Allocate a large area for the TLS. */ #define RSEQ_THREAD_AREA_ALLOC_SIZE 1024 @@ -126,6 +125,32 @@ unsigned get_rseq_min_alloc_size(void) return alloc_size; } +/* + * Return the feature size supported by the kernel. + * + * Depending on the value returned by getauxval(AT_RSEQ_FEATURE_SIZE): + * + * 0: Return ORIG_RSEQ_FEATURE_SIZE (20) + * > 0: Return the value from getauxval(AT_RSEQ_FEATURE_SIZE). + * + * It should never return a value below ORIG_RSEQ_FEATURE_SIZE. + */ +static +unsigned int get_rseq_kernel_feature_size(void) +{ + unsigned long auxv_rseq_feature_size, auxv_rseq_align; + + auxv_rseq_align = getauxval(AT_RSEQ_ALIGN); + assert(!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE); + + auxv_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE); + assert(!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE); + if (auxv_rseq_feature_size) + return auxv_rseq_feature_size; + else + return ORIG_RSEQ_FEATURE_SIZE; +} + int rseq_register_current_thread(void) { int rc; @@ -138,14 +163,27 @@ int rseq_register_current_thread(void) } rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG); if (rc) { - if (RSEQ_READ_ONCE(rseq_reg_success)) { + /* + * After at least one thread has registered successfully + * (rseq_size > 0), the registration of other threads should + * never fail. + */ + if (RSEQ_READ_ONCE(rseq_size) > 0) { /* Incoherent success/failure within process. */ abort(); } return -1; } assert(rseq_current_cpu_raw() >= 0); - RSEQ_WRITE_ONCE(rseq_reg_success, 1); + + /* + * The first thread to register sets the rseq_size to mimic the libc + * behavior. + */ + if (RSEQ_READ_ONCE(rseq_size) == 0) { + RSEQ_WRITE_ONCE(rseq_size, get_rseq_kernel_feature_size()); + } + return 0; } @@ -163,32 +201,6 @@ int rseq_unregister_current_thread(void) return 0; } -/* - * Return the feature size supported by the kernel. - * - * Depending on the value returned by getauxval(AT_RSEQ_FEATURE_SIZE): - * - * 0: Return ORIG_RSEQ_FEATURE_SIZE (20) - * > 0: Return the value from getauxval(AT_RSEQ_FEATURE_SIZE). - * - * It should never return a value below ORIG_RSEQ_FEATURE_SIZE. - */ -static -unsigned int get_rseq_kernel_feature_size(void) -{ - unsigned long auxv_rseq_feature_size, auxv_rseq_align; - - auxv_rseq_align = getauxval(AT_RSEQ_ALIGN); - assert(!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE); - - auxv_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE); - assert(!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE); - if (auxv_rseq_feature_size) - return auxv_rseq_feature_size; - else - return ORIG_RSEQ_FEATURE_SIZE; -} - /* * Initialize the public symbols for the rseq offset, size, feature size and * flags prior to registering threads. If glibc owns the registration, get the @@ -269,12 +281,11 @@ void rseq_init(void) /* rseq flags are deprecated, always set to 0. */ rseq_flags = 0; - /* Check if the rseq syscall is available, if not set the size to 0. */ - if (!rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) { - rseq_size = 0; - goto unlock; - } - rseq_size = get_rseq_kernel_feature_size(); + /* + * Set the size to 0 until at least one thread registers to mimic the + * libc behavior. + */ + rseq_size = 0; unlock: pthread_mutex_unlock(&init_lock); } diff --git a/tests/basic_test.c b/tests/basic_test.c index 743f7a5..fcde879 100644 --- a/tests/basic_test.c +++ b/tests/basic_test.c @@ -18,6 +18,19 @@ #include "tap.h" +static void test_registered(void) +{ + struct rseq_abi *rseq_abi = rseq_get_abi(); + + ok(rseq_flags == 0, "rseq_flags after registration is 0 (%d)", rseq_flags); + ok(rseq_size >= 20, "rseq_size after registration is 20 or greater (%d)", rseq_size); + ok(rseq_offset != 0, "rseq_offset after registration is not 0 (%td)", rseq_offset); + + ok((int32_t) rseq_abi->cpu_id >= 0, + "rseq->cpu_id after registration is 0 or greater (%d)", + (int32_t) rseq_abi->cpu_id); +} + static void test_cpu_pointer(void) { cpu_set_t affinity, test_affinity; @@ -70,6 +83,7 @@ int main(void) pass("Registered current thread with rseq"); } + test_registered(); test_cpu_pointer(); if (rseq_unregister_current_thread()) { diff --git a/tests/unregistered_test.c b/tests/unregistered_test.c index 96497a9..fc7c182 100644 --- a/tests/unregistered_test.c +++ b/tests/unregistered_test.c @@ -31,7 +31,7 @@ int main(void) /* The syscall is available but the current thread is not registered. */ ok(rseq_flags == 0, "rseq_flags prior to registration is 0 (%d)", rseq_flags); - ok(rseq_size >= 20, "rseq_size prior to registration is 20 or greater (%d)", rseq_size); + ok(rseq_size == 0, "rseq_size prior to registration is 0 (%d)", rseq_size); ok(rseq_offset != 0, "rseq_offset prior to registration is not 0 (%td)", rseq_offset); rseq_abi = rseq_get_abi();