Adapt to glibc __rseq_size feature detection
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Tue, 9 Jul 2024 15:23:09 +0000 (11:23 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Tue, 23 Jul 2024 15:20:24 +0000 (11:20 -0400)
Adapt the rseq.c code to follow GNU C library changes introduced by:

commit 2e456ccf0c34 ("Linux: Make __rseq_size useful for feature detection (bug 31965)")

Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Change-Id: I83990c9c3b1e9277af48edbfc3176d3dc5160d3c

include/rseq/rseq.h
src/rseq.c
tests/no_syscall_test.c
tests/unregistered_test.c

index 8db0b899b2d24a4606df4c4704f8596e6bdde714..d8c7ef3f381637c620e5e9e78220fdca176ad3a3 100644 (file)
@@ -59,7 +59,18 @@ extern "C" {
 extern ptrdiff_t rseq_offset;
 
 /*
- * Size of the registered rseq area. 0 if the registration was
+ * The rseq ABI is composed of extensible feature fields. The extensions
+ * are done by appending additional fields at the end of the structure.
+ * The rseq_size defines the size of the active feature set which can be
+ * used by the application for the current rseq registration. Features
+ * starting at offset >= rseq_size are inactive and should not be used.
+ *
+ * The rseq_size is the intersection between the available allocation
+ * size for the rseq area and the feature size supported by the kernel.
+ */
+
+/*
+ * Size of the active rseq feature set. 0 if the registration was
  * unsuccessful.
  */
 extern unsigned int rseq_size;
@@ -67,12 +78,6 @@ extern unsigned int rseq_size;
 /* Flags used during rseq registration. */
 extern unsigned int rseq_flags;
 
-/*
- * rseq feature size supported by the kernel. 0 if the registration was
- * unsuccessful.
- */
-extern unsigned int rseq_feature_size;
-
 /*
  * Returns a pointer to the rseq area.
  */
@@ -177,7 +182,7 @@ uint32_t rseq_current_cpu(void)
 static inline __attribute__((always_inline))
 bool rseq_node_id_available(void)
 {
-       return (int) rseq_feature_size >= (int) rseq_offsetofend(struct rseq_abi, node_id);
+       return (int) rseq_size >= (int) rseq_offsetofend(struct rseq_abi, node_id);
 }
 
 /*
@@ -193,7 +198,7 @@ uint32_t rseq_current_node_id(void)
 static inline __attribute__((always_inline))
 bool rseq_mm_cid_available(void)
 {
-       return (int) rseq_feature_size >= (int) rseq_offsetofend(struct rseq_abi, mm_cid);
+       return (int) rseq_size >= (int) rseq_offsetofend(struct rseq_abi, mm_cid);
 }
 
 static inline __attribute__((always_inline))
index ae72eaa22f556b64a0d763eaa179e9e4960c0162..61f353271e94b510adf811a0dd7b639f94a2dec6 100644 (file)
@@ -45,7 +45,7 @@ static const unsigned int *libc_rseq_flags_p;
 ptrdiff_t rseq_offset;
 
 /*
- * Size of the registered rseq area. 0 if the registration was
+ * Size of the active rseq feature set. 0 if the registration was
  * unsuccessful.
  */
 unsigned int rseq_size = -1U;
@@ -53,12 +53,6 @@ unsigned int rseq_size = -1U;
 /* Flags used during rseq registration. */
 unsigned int rseq_flags;
 
-/*
- * rseq feature size supported by the kernel. 0 if the registration was
- * unsuccessful.
- */
-unsigned int rseq_feature_size = -1U;
-
 static int rseq_ownership;
 static int rseq_reg_success;   /* At least one rseq registration has succeded. */
 
@@ -121,6 +115,17 @@ bool rseq_available(unsigned int query)
        return false;
 }
 
+/* The rseq areas need to be at least 32 bytes. */
+static
+unsigned get_rseq_min_alloc_size(void)
+{
+       unsigned int alloc_size = rseq_size;
+
+       if (alloc_size < ORIG_RSEQ_ALLOC_SIZE)
+               alloc_size = ORIG_RSEQ_ALLOC_SIZE;
+       return alloc_size;
+}
+
 int rseq_register_current_thread(void)
 {
        int rc;
@@ -131,7 +136,7 @@ int rseq_register_current_thread(void)
                /* Treat libc's ownership as a successful registration. */
                return 0;
        }
-       rc = sys_rseq(&__rseq_abi, rseq_size, 0, RSEQ_SIG);
+       rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG);
        if (rc) {
                if (RSEQ_READ_ONCE(rseq_reg_success)) {
                        /* Incoherent success/failure within process. */
@@ -152,14 +157,24 @@ int rseq_unregister_current_thread(void)
                /* Treat libc's ownership as a successful unregistration. */
                return 0;
        }
-       rc = sys_rseq(&__rseq_abi, rseq_size, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
+       rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
        if (rc)
                return -1;
        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_feature_size(void)
+unsigned int get_rseq_kernel_feature_size(void)
 {
        unsigned long auxv_rseq_feature_size, auxv_rseq_align;
 
@@ -204,20 +219,44 @@ void rseq_init(void)
        libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
        if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p &&
                        *libc_rseq_size_p != 0) {
+               unsigned int libc_rseq_size;
+
                /* rseq registration owned by glibc */
                rseq_offset = *libc_rseq_offset_p;
-               rseq_size = *libc_rseq_size_p;
+               libc_rseq_size = *libc_rseq_size_p;
                rseq_flags = *libc_rseq_flags_p;
-               rseq_feature_size = get_rseq_feature_size();
 
                /*
-                * The registered rseq area could be smaller than the feature
-                * size reported by the kernel auxval. Cap it to the rseq size
-                * so we don't try to access features past the end of the rseq
-                * area.
+                * Previous versions of glibc expose the value
+                * 32 even though the kernel only supported 20
+                * bytes initially. Therefore treat 32 as a
+                * special-case. glibc 2.40 exposes a 20 bytes
+                * __rseq_size without using getauxval(3) to
+                * query the supported size, while still allocating a 32
+                * bytes area. Also treat 20 as a special-case.
+                *
+                * Special-cases are handled by using the following
+                * value as active feature set size:
+                *
+                *   rseq_size = min(32, get_rseq_kernel_feature_size())
                 */
-               if (rseq_feature_size > rseq_size)
-                       rseq_feature_size = rseq_size;
+               switch (libc_rseq_size) {
+               case ORIG_RSEQ_FEATURE_SIZE:    /* Fallthrough. */
+               case ORIG_RSEQ_ALLOC_SIZE:
+               {
+                       unsigned int rseq_kernel_feature_size = get_rseq_kernel_feature_size();
+
+                       if (rseq_kernel_feature_size < ORIG_RSEQ_ALLOC_SIZE)
+                               rseq_size = rseq_kernel_feature_size;
+                       else
+                               rseq_size = ORIG_RSEQ_ALLOC_SIZE;
+                       break;
+               }
+               default:
+                       /* Otherwise just use the __rseq_size from libc as rseq_size. */
+                       rseq_size = libc_rseq_size;
+                       break;
+               }
                goto unlock;
        }
 
@@ -230,26 +269,12 @@ 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 and
-        * feature_size to 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;
-               rseq_feature_size = 0;
                goto unlock;
        }
-
-       /*
-        * If the feature size matches the original ABI (20), set the size to
-        * match the original ABI allocation (32), otherwise use the allocated
-        * size.
-        */
-       rseq_feature_size = get_rseq_feature_size();
-       if (rseq_feature_size == ORIG_RSEQ_FEATURE_SIZE)
-               rseq_size = ORIG_RSEQ_ALLOC_SIZE;
-       else
-               rseq_size = RSEQ_THREAD_AREA_ALLOC_SIZE;
+       rseq_size = get_rseq_kernel_feature_size();
 unlock:
        pthread_mutex_unlock(&init_lock);
 }
@@ -261,7 +286,6 @@ void rseq_exit(void)
                return;
        rseq_offset = 0;
        rseq_size = -1U;
-       rseq_feature_size = -1U;
        rseq_ownership = 0;
 }
 
index b116012422c8709e423b0d4f9a52abd36d089b3f..a682456faf3a70baf5ff73b7c6ccf0cc37f0c069 100644 (file)
@@ -34,7 +34,6 @@ int main(void)
 
        ok(rseq_flags == 0, "rseq_flags prior to registration is 0 (%d)", rseq_flags);
        ok(rseq_size == 0, "rseq_size prior to registration is 0 (%d)", rseq_size);
-       ok(rseq_feature_size == 0, "rseq_feature_size prior to registration is 0 (%d)", rseq_feature_size);
        ok(rseq_offset != 0, "rseq_offset prior to registration is not 0 (%td)", rseq_offset);
 
        rseq_abi = rseq_get_abi();
index fc45dab02bbc627dc5f1f01803b0e7b8adf64171..96497a949977c403fc5f4902ec3e9c3168ce9a0a 100644 (file)
@@ -10,7 +10,7 @@
 
 #include "tap.h"
 
-#define NR_TESTS 5
+#define NR_TESTS 4
 
 /*
  * Check the state of the public symbols when the rseq syscall is available but
@@ -31,8 +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 >= 32, "rseq_size prior to registration is 32 or greater (%d)", rseq_size);
-       ok(rseq_feature_size >= 20, "rseq_feature_size prior to registration is 20 or greater (%d)", rseq_feature_size);
+       ok(rseq_size >= 20, "rseq_size prior to registration is 20 or greater (%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.028014 seconds and 4 git commands to generate.