]> www.infradead.org Git - users/hch/misc.git/commitdiff
selftests/rseq: Fix mm_cid test failure
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Wed, 9 Oct 2024 01:28:01 +0000 (21:28 -0400)
committerShuah Khan <skhan@linuxfoundation.org>
Fri, 11 Oct 2024 21:05:05 +0000 (15:05 -0600)
Adapt the rseq.c/rseq.h code to follow GNU C library changes introduced by:

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

Without this fix, rseq selftests for mm_cid fail:

./run_param_test.sh
Default parameters
Running test spinlock
Running compare-twice test spinlock
Running mm_cid test spinlock
Error: cpu id getter unavailable

Fixes: 18c2355838e7 ("selftests/rseq: Implement rseq mm_cid field support")
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Peter Zijlstra <peterz@infradead.org>
CC: Boqun Feng <boqun.feng@gmail.com>
CC: "Paul E. McKenney" <paulmck@kernel.org>
Cc: Shuah Khan <skhan@linuxfoundation.org>
CC: Carlos O'Donell <carlos@redhat.com>
CC: Florian Weimer <fweimer@redhat.com>
CC: linux-kselftest@vger.kernel.org
CC: stable@vger.kernel.org
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
tools/testing/selftests/rseq/rseq.c
tools/testing/selftests/rseq/rseq.h

index 96e812bdf8a45c7ad7cc0f8d1e8b2b26d6570722..5b9772cdf2651bb924e1c68d50eea83d9c1a3e96 100644 (file)
@@ -60,12 +60,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. */
 
@@ -111,6 +105,43 @@ int rseq_available(void)
        }
 }
 
+/* The rseq areas need to be at least 32 bytes. */
+static
+unsigned int 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;
+}
+
+/*
+ * 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;
@@ -119,7 +150,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. */
@@ -140,28 +171,12 @@ 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;
 }
 
-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)
 {
@@ -178,28 +193,54 @@ void rseq_init(void)
        }
        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();
-               if (rseq_feature_size > rseq_size)
-                       rseq_feature_size = rseq_size;
+
+               /*
+                * 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())
+                */
+               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;
+               }
                return;
        }
        rseq_ownership = 1;
        if (!rseq_available()) {
                rseq_size = 0;
-               rseq_feature_size = 0;
                return;
        }
        rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer();
        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))
@@ -209,7 +250,6 @@ void rseq_exit(void)
                return;
        rseq_offset = 0;
        rseq_size = -1U;
-       rseq_feature_size = -1U;
        rseq_ownership = 0;
 }
 
index d7364ea4d201d206709af51c49d8c7b8e4936c81..4e217b620e0c7a9743b0db06dfd22e3cbdc72d08 100644 (file)
@@ -68,12 +68,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;
-
 enum rseq_mo {
        RSEQ_MO_RELAXED = 0,
        RSEQ_MO_CONSUME = 1,    /* Unused */
@@ -193,7 +187,7 @@ static inline uint32_t rseq_current_cpu(void)
 
 static inline bool rseq_node_id_available(void)
 {
-       return (int) rseq_feature_size >= rseq_offsetofend(struct rseq_abi, node_id);
+       return (int) rseq_size >= rseq_offsetofend(struct rseq_abi, node_id);
 }
 
 /*
@@ -207,7 +201,7 @@ static inline uint32_t rseq_current_node_id(void)
 
 static inline bool rseq_mm_cid_available(void)
 {
-       return (int) rseq_feature_size >= rseq_offsetofend(struct rseq_abi, mm_cid);
+       return (int) rseq_size >= rseq_offsetofend(struct rseq_abi, mm_cid);
 }
 
 static inline uint32_t rseq_current_mm_cid(void)