selftests/rseq: Fix mm_cid test failure

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>

authored by Mathieu Desnoyers and committed by Shuah Khan a0cc6493 34d5b600

+77 -43
+75 -35
tools/testing/selftests/rseq/rseq.c
··· 60 /* Flags used during rseq registration. */ 61 unsigned int rseq_flags; 62 63 - /* 64 - * rseq feature size supported by the kernel. 0 if the registration was 65 - * unsuccessful. 66 - */ 67 - unsigned int rseq_feature_size = -1U; 68 - 69 static int rseq_ownership; 70 static int rseq_reg_success; /* At least one rseq registration has succeded. */ 71 ··· 105 } 106 } 107 108 int rseq_register_current_thread(void) 109 { 110 int rc; ··· 150 /* Treat libc's ownership as a successful registration. */ 151 return 0; 152 } 153 - rc = sys_rseq(&__rseq_abi, rseq_size, 0, RSEQ_SIG); 154 if (rc) { 155 if (RSEQ_READ_ONCE(rseq_reg_success)) { 156 /* Incoherent success/failure within process. */ ··· 171 /* Treat libc's ownership as a successful unregistration. */ 172 return 0; 173 } 174 - rc = sys_rseq(&__rseq_abi, rseq_size, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG); 175 if (rc) 176 return -1; 177 return 0; 178 - } 179 - 180 - static 181 - unsigned int get_rseq_feature_size(void) 182 - { 183 - unsigned long auxv_rseq_feature_size, auxv_rseq_align; 184 - 185 - auxv_rseq_align = getauxval(AT_RSEQ_ALIGN); 186 - assert(!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE); 187 - 188 - auxv_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE); 189 - assert(!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE); 190 - if (auxv_rseq_feature_size) 191 - return auxv_rseq_feature_size; 192 - else 193 - return ORIG_RSEQ_FEATURE_SIZE; 194 } 195 196 static __attribute__((constructor)) ··· 193 } 194 if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p && 195 *libc_rseq_size_p != 0) { 196 /* rseq registration owned by glibc */ 197 rseq_offset = *libc_rseq_offset_p; 198 - rseq_size = *libc_rseq_size_p; 199 rseq_flags = *libc_rseq_flags_p; 200 - rseq_feature_size = get_rseq_feature_size(); 201 - if (rseq_feature_size > rseq_size) 202 - rseq_feature_size = rseq_size; 203 return; 204 } 205 rseq_ownership = 1; 206 if (!rseq_available()) { 207 rseq_size = 0; 208 - rseq_feature_size = 0; 209 return; 210 } 211 rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer(); 212 rseq_flags = 0; 213 - rseq_feature_size = get_rseq_feature_size(); 214 - if (rseq_feature_size == ORIG_RSEQ_FEATURE_SIZE) 215 - rseq_size = ORIG_RSEQ_ALLOC_SIZE; 216 - else 217 - rseq_size = RSEQ_THREAD_AREA_ALLOC_SIZE; 218 } 219 220 static __attribute__((destructor)) ··· 250 return; 251 rseq_offset = 0; 252 rseq_size = -1U; 253 - rseq_feature_size = -1U; 254 rseq_ownership = 0; 255 } 256
··· 60 /* Flags used during rseq registration. */ 61 unsigned int rseq_flags; 62 63 static int rseq_ownership; 64 static int rseq_reg_success; /* At least one rseq registration has succeded. */ 65 ··· 111 } 112 } 113 114 + /* The rseq areas need to be at least 32 bytes. */ 115 + static 116 + unsigned int get_rseq_min_alloc_size(void) 117 + { 118 + unsigned int alloc_size = rseq_size; 119 + 120 + if (alloc_size < ORIG_RSEQ_ALLOC_SIZE) 121 + alloc_size = ORIG_RSEQ_ALLOC_SIZE; 122 + return alloc_size; 123 + } 124 + 125 + /* 126 + * Return the feature size supported by the kernel. 127 + * 128 + * Depending on the value returned by getauxval(AT_RSEQ_FEATURE_SIZE): 129 + * 130 + * 0: Return ORIG_RSEQ_FEATURE_SIZE (20) 131 + * > 0: Return the value from getauxval(AT_RSEQ_FEATURE_SIZE). 132 + * 133 + * It should never return a value below ORIG_RSEQ_FEATURE_SIZE. 134 + */ 135 + static 136 + unsigned int get_rseq_kernel_feature_size(void) 137 + { 138 + unsigned long auxv_rseq_feature_size, auxv_rseq_align; 139 + 140 + auxv_rseq_align = getauxval(AT_RSEQ_ALIGN); 141 + assert(!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE); 142 + 143 + auxv_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE); 144 + assert(!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE); 145 + if (auxv_rseq_feature_size) 146 + return auxv_rseq_feature_size; 147 + else 148 + return ORIG_RSEQ_FEATURE_SIZE; 149 + } 150 + 151 int rseq_register_current_thread(void) 152 { 153 int rc; ··· 119 /* Treat libc's ownership as a successful registration. */ 120 return 0; 121 } 122 + rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG); 123 if (rc) { 124 if (RSEQ_READ_ONCE(rseq_reg_success)) { 125 /* Incoherent success/failure within process. */ ··· 140 /* Treat libc's ownership as a successful unregistration. */ 141 return 0; 142 } 143 + rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG); 144 if (rc) 145 return -1; 146 return 0; 147 } 148 149 static __attribute__((constructor)) ··· 178 } 179 if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p && 180 *libc_rseq_size_p != 0) { 181 + unsigned int libc_rseq_size; 182 + 183 /* rseq registration owned by glibc */ 184 rseq_offset = *libc_rseq_offset_p; 185 + libc_rseq_size = *libc_rseq_size_p; 186 rseq_flags = *libc_rseq_flags_p; 187 + 188 + /* 189 + * Previous versions of glibc expose the value 190 + * 32 even though the kernel only supported 20 191 + * bytes initially. Therefore treat 32 as a 192 + * special-case. glibc 2.40 exposes a 20 bytes 193 + * __rseq_size without using getauxval(3) to 194 + * query the supported size, while still allocating a 32 195 + * bytes area. Also treat 20 as a special-case. 196 + * 197 + * Special-cases are handled by using the following 198 + * value as active feature set size: 199 + * 200 + * rseq_size = min(32, get_rseq_kernel_feature_size()) 201 + */ 202 + switch (libc_rseq_size) { 203 + case ORIG_RSEQ_FEATURE_SIZE: 204 + fallthrough; 205 + case ORIG_RSEQ_ALLOC_SIZE: 206 + { 207 + unsigned int rseq_kernel_feature_size = get_rseq_kernel_feature_size(); 208 + 209 + if (rseq_kernel_feature_size < ORIG_RSEQ_ALLOC_SIZE) 210 + rseq_size = rseq_kernel_feature_size; 211 + else 212 + rseq_size = ORIG_RSEQ_ALLOC_SIZE; 213 + break; 214 + } 215 + default: 216 + /* Otherwise just use the __rseq_size from libc as rseq_size. */ 217 + rseq_size = libc_rseq_size; 218 + break; 219 + } 220 return; 221 } 222 rseq_ownership = 1; 223 if (!rseq_available()) { 224 rseq_size = 0; 225 return; 226 } 227 rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer(); 228 rseq_flags = 0; 229 } 230 231 static __attribute__((destructor)) ··· 209 return; 210 rseq_offset = 0; 211 rseq_size = -1U; 212 rseq_ownership = 0; 213 } 214
+2 -8
tools/testing/selftests/rseq/rseq.h
··· 68 /* Flags used during rseq registration. */ 69 extern unsigned int rseq_flags; 70 71 - /* 72 - * rseq feature size supported by the kernel. 0 if the registration was 73 - * unsuccessful. 74 - */ 75 - extern unsigned int rseq_feature_size; 76 - 77 enum rseq_mo { 78 RSEQ_MO_RELAXED = 0, 79 RSEQ_MO_CONSUME = 1, /* Unused */ ··· 187 188 static inline bool rseq_node_id_available(void) 189 { 190 - return (int) rseq_feature_size >= rseq_offsetofend(struct rseq_abi, node_id); 191 } 192 193 /* ··· 201 202 static inline bool rseq_mm_cid_available(void) 203 { 204 - return (int) rseq_feature_size >= rseq_offsetofend(struct rseq_abi, mm_cid); 205 } 206 207 static inline uint32_t rseq_current_mm_cid(void)
··· 68 /* Flags used during rseq registration. */ 69 extern unsigned int rseq_flags; 70 71 enum rseq_mo { 72 RSEQ_MO_RELAXED = 0, 73 RSEQ_MO_CONSUME = 1, /* Unused */ ··· 193 194 static inline bool rseq_node_id_available(void) 195 { 196 + return (int) rseq_size >= rseq_offsetofend(struct rseq_abi, node_id); 197 } 198 199 /* ··· 207 208 static inline bool rseq_mm_cid_available(void) 209 { 210 + return (int) rseq_size >= rseq_offsetofend(struct rseq_abi, mm_cid); 211 } 212 213 static inline uint32_t rseq_current_mm_cid(void)