selftests/rseq: Use ELF auxiliary vector for extensible rseq
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Tue, 22 Nov 2022 20:39:07 +0000 (15:39 -0500)
committerPeter Zijlstra <peterz@infradead.org>
Tue, 27 Dec 2022 11:52:11 +0000 (12:52 +0100)
Use the ELF auxiliary vector AT_RSEQ_FEATURE_SIZE to detect the RSEQ
features supported by the kernel.

Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20221122203932.231377-6-mathieu.desnoyers@efficios.com
tools/testing/selftests/rseq/rseq-abi.h
tools/testing/selftests/rseq/rseq.c
tools/testing/selftests/rseq/rseq.h

index a8c44d9af71fb81a6020a1d4a144b6776aa3fd2f..00ac846d85b0bb982b9202fe31b14c1401ee7b53 100644 (file)
@@ -146,6 +146,11 @@ struct rseq_abi {
         *     this thread.
         */
        __u32 flags;
+
+       /*
+        * Flexible array member at end of structure, after last feature field.
+        */
+       char end[];
 } __attribute__((aligned(4 * sizeof(__u64))));
 
 #endif /* _RSEQ_ABI_H */
index 376a73f1ac4131e2a09ceea09116d7b27e1ef858..1e8e3265bdbf8d561ebe1058276f1381e9fa0f76 100644 (file)
@@ -28,6 +28,8 @@
 #include <limits.h>
 #include <dlfcn.h>
 #include <stddef.h>
+#include <sys/auxv.h>
+#include <linux/auxvec.h>
 
 #include "../kselftest.h"
 #include "rseq.h"
@@ -36,20 +38,38 @@ static const ptrdiff_t *libc_rseq_offset_p;
 static const unsigned int *libc_rseq_size_p;
 static const unsigned int *libc_rseq_flags_p;
 
-/* Offset from the thread pointer to the rseq area.  */
+/* Offset from the thread pointer to the rseq area. */
 ptrdiff_t rseq_offset;
 
-/* Size of the registered rseq area.  0 if the registration was
-   unsuccessful.  */
+/*
+ * Size of the registered rseq area. 0 if the registration was
+ * unsuccessful.
+ */
 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. */
+
+/* Allocate a large area for the TLS. */
+#define RSEQ_THREAD_AREA_ALLOC_SIZE    1024
+
+/* Original struct rseq feature size is 20 bytes. */
+#define ORIG_RSEQ_FEATURE_SIZE         20
+
+/* Original struct rseq allocation size is 32 bytes. */
+#define ORIG_RSEQ_ALLOC_SIZE           32
 
 static
-__thread struct rseq_abi __rseq_abi __attribute__((tls_model("initial-exec"))) = {
+__thread struct rseq_abi __rseq_abi __attribute__((tls_model("initial-exec"), aligned(RSEQ_THREAD_AREA_ALLOC_SIZE))) = {
        .cpu_id = RSEQ_ABI_CPU_ID_UNINITIALIZED,
 };
 
@@ -84,10 +104,16 @@ int rseq_register_current_thread(void)
                /* Treat libc's ownership as a successful registration. */
                return 0;
        }
-       rc = sys_rseq(&__rseq_abi, sizeof(struct rseq_abi), 0, RSEQ_SIG);
-       if (rc)
+       rc = sys_rseq(&__rseq_abi, rseq_size, 0, RSEQ_SIG);
+       if (rc) {
+               if (RSEQ_READ_ONCE(rseq_reg_success)) {
+                       /* Incoherent success/failure within process. */
+                       abort();
+               }
                return -1;
+       }
        assert(rseq_current_cpu_raw() >= 0);
+       RSEQ_WRITE_ONCE(rseq_reg_success, 1);
        return 0;
 }
 
@@ -99,12 +125,28 @@ int rseq_unregister_current_thread(void)
                /* Treat libc's ownership as a successful unregistration. */
                return 0;
        }
-       rc = sys_rseq(&__rseq_abi, sizeof(struct rseq_abi), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
+       rc = sys_rseq(&__rseq_abi, rseq_size, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
        if (rc)
                return -1;
        return 0;
 }
 
+static
+unsigned int get_rseq_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;
+}
+
 static __attribute__((constructor))
 void rseq_init(void)
 {
@@ -117,16 +159,24 @@ void rseq_init(void)
                rseq_offset = *libc_rseq_offset_p;
                rseq_size = *libc_rseq_size_p;
                rseq_flags = *libc_rseq_flags_p;
+               rseq_feature_size = get_rseq_feature_size();
+               if (rseq_feature_size > rseq_size)
+                       rseq_feature_size = rseq_size;
                return;
        }
        rseq_ownership = 1;
        if (!rseq_available()) {
                rseq_size = 0;
+               rseq_feature_size = 0;
                return;
        }
        rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer();
-       rseq_size = sizeof(struct rseq_abi);
        rseq_flags = 0;
+       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;
 }
 
 static __attribute__((destructor))
@@ -136,6 +186,7 @@ void rseq_exit(void)
                return;
        rseq_offset = 0;
        rseq_size = -1U;
+       rseq_feature_size = -1U;
        rseq_ownership = 0;
 }
 
index 6f7513384bf503e0aea7eabe87073b33b6eff97b..95adc1e1b0db689e33538b36bbdc339ba889c735 100644 (file)
 
 #include "rseq-thread-pointer.h"
 
-/* Offset from the thread pointer to the rseq area.  */
+/* Offset from the thread pointer to the rseq area. */
 extern ptrdiff_t rseq_offset;
-/* Size of the registered rseq area.  0 if the registration was
-   unsuccessful.  */
+
+/*
+ * Size of the registered rseq area. 0 if the registration was
+ * unsuccessful.
+ */
 extern unsigned int rseq_size;
-/* Flags used during rseq registration.  */
+
+/* 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;
+
 static inline struct rseq_abi *rseq_get_abi(void)
 {
        return (struct rseq_abi *) ((uintptr_t) rseq_thread_pointer() + rseq_offset);