From: Mathieu Desnoyers Date: Tue, 9 Jul 2024 15:23:09 +0000 (-0400) Subject: Adapt to glibc __rseq_size feature detection X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=c7b45750fa85eeaed7dfd2fde9491d62cb8c3e19;p=librseq.git Adapt to glibc __rseq_size feature detection 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 Change-Id: I83990c9c3b1e9277af48edbfc3176d3dc5160d3c --- diff --git a/include/rseq/rseq.h b/include/rseq/rseq.h index 8db0b89..d8c7ef3 100644 --- a/include/rseq/rseq.h +++ b/include/rseq/rseq.h @@ -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)) diff --git a/src/rseq.c b/src/rseq.c index ae72eaa..61f3532 100644 --- a/src/rseq.c +++ b/src/rseq.c @@ -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; } diff --git a/tests/no_syscall_test.c b/tests/no_syscall_test.c index b116012..a682456 100644 --- a/tests/no_syscall_test.c +++ b/tests/no_syscall_test.c @@ -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(); diff --git a/tests/unregistered_test.c b/tests/unregistered_test.c index fc45dab..96497a9 100644 --- a/tests/unregistered_test.c +++ b/tests/unregistered_test.c @@ -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();