Only set 'rseq_size' on first thread registration
authorMichael Jeanson <mjeanson@efficios.com>
Fri, 26 Jul 2024 19:50:54 +0000 (19:50 +0000)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Mon, 29 Jul 2024 19:23:22 +0000 (15:23 -0400)
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 <mjeanson@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
src/rseq.c
tests/basic_test.c
tests/unregistered_test.c

index 61f353271e94b510adf811a0dd7b639f94a2dec6..7ee3616e0463b11d1d0d55693779434ae6277e87 100644 (file)
@@ -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);
 }
index 743f7a5fecdfa9adbba80fc8d17f62f307517988..fcde879bc29974634caf445a962e1328f6b301e4 100644 (file)
 
 #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()) {
index 96497a949977c403fc5f4902ec3e9c3168ce9a0a..fc7c18270d29ad7499d5116d13a4ce0e72607085 100644 (file)
@@ -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();
This page took 0.026079 seconds and 4 git commands to generate.