From c7dee570720d657f9d866586c4e0681c73626689 Mon Sep 17 00:00:00 2001 From: Hayodea Hekol Date: Sat, 30 May 2026 10:42:47 -0400 Subject: [PATCH] Bmach: rseqsliceprobe: improve output --- buildmach/rseqsliceprobe.cpp | 186 ++++++++++++++++++++++++++++++----- 1 file changed, 162 insertions(+), 24 deletions(-) diff --git a/buildmach/rseqsliceprobe.cpp b/buildmach/rseqsliceprobe.cpp index d41c655..33e8990 100644 --- a/buildmach/rseqsliceprobe.cpp +++ b/buildmach/rseqsliceprobe.cpp @@ -11,8 +11,13 @@ #include #include #include +#include #include +#ifndef ENOTSUPP +#define ENOTSUPP 524 +#endif + #if defined(__x86_64__) #ifndef ARCH_GET_FS #define ARCH_GET_FS 0x1003 @@ -83,15 +88,33 @@ struct rseq_compat { uint8_t reserved; } __attribute__((aligned(32))); +struct prctl_probe_result { + bool ok; + int value; + int err; +}; + alignas(32) static thread_local unsigned char local_rseq_storage[512]; static unsigned int g_rseq_feature_size; static unsigned int g_rseq_alloc_size; static unsigned int g_rseq_align; +static unsigned long g_aux_rseq_feature_size; +static unsigned long g_aux_rseq_align; static struct rseq *g_registered_rseq; static struct rseq_compat *g_registered_rseq_compat; static bool g_own_registration; +static const char *yes_no(bool value) +{ + return value ? "yes" : "no"; +} + +static const char *set_clear(bool value) +{ + return value ? "set" : "clear"; +} + static int get_thread_pointer(uintptr_t *tp_out) { #if defined(__x86_64__) @@ -147,6 +170,7 @@ static const char *errno_name(int err) case EINVAL: return "EINVAL"; case ENOSYS: return "ENOSYS"; case ENOTSUP: return "ENOTSUP"; + case ENOTSUPP: return "ENOTSUPP"; case ENXIO: return "ENXIO"; case EPERM: return "EPERM"; case EBUSY: return "EBUSY"; @@ -154,6 +178,26 @@ static const char *errno_name(int err) } } +static const char *prctl_failure_meaning(int err) +{ + switch (err) { + case EINVAL: + return "the prctl operation or argument is not accepted by this kernel"; + case ENOTSUPP: + return "the prctl operation exists, but the slice extension is not supported here"; + case EPERM: + return "the kernel denied the requested operation"; + default: + return "the kernel returned an unclassified failure"; + } +} + +static void print_errno_status(const char *label, int err) +{ + std::printf("%s: errno=%d (%s: %s)\n", + label, err, errno_name(err), std::strerror(err)); +} + static unsigned int max_u32(unsigned int a, unsigned int b) { return a > b ? a : b; @@ -174,11 +218,13 @@ static void print_registration_source(void) static int setup_rseq(void) { - unsigned long aux_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE); - unsigned long aux_align = getauxval(AT_RSEQ_ALIGN); + g_aux_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE); + g_aux_rseq_align = getauxval(AT_RSEQ_ALIGN); - g_rseq_feature_size = aux_feature_size ? static_cast(aux_feature_size) : 20U; - g_rseq_align = aux_align ? static_cast(aux_align) : 32U; + g_rseq_feature_size = g_aux_rseq_feature_size ? + static_cast(g_aux_rseq_feature_size) : 20U; + g_rseq_align = g_aux_rseq_align ? + static_cast(g_aux_rseq_align) : 32U; g_rseq_alloc_size = max_u32(g_rseq_feature_size, 32U); if (&__rseq_size != nullptr && __rseq_size != 0) { @@ -191,6 +237,7 @@ static int setup_rseq(void) g_registered_rseq = reinterpret_cast(tp + __rseq_offset); g_registered_rseq_compat = reinterpret_cast(tp + __rseq_offset); + g_own_registration = false; if (__rseq_size < g_rseq_feature_size) g_rseq_feature_size = __rseq_size; if (__rseq_size > g_rseq_alloc_size) @@ -237,22 +284,116 @@ static void teardown_rseq(void) std::perror("rseq unregister"); } +static prctl_probe_result probe_prctl_get(void) +{ + errno = 0; + int rc = prctl(PR_RSEQ_SLICE_EXTENSION, PR_RSEQ_SLICE_EXTENSION_GET, + 0UL, 0UL, 0UL); + int saved_errno = errno; + + if (rc >= 0) + return { true, rc, 0 }; + return { false, -1, saved_errno }; +} + +static prctl_probe_result probe_prctl_set(unsigned long value) +{ + errno = 0; + int rc = prctl(PR_RSEQ_SLICE_EXTENSION, PR_RSEQ_SLICE_EXTENSION_SET, + value, 0UL, 0UL); + int saved_errno = errno; + + if (rc >= 0) + return { true, rc, 0 }; + return { false, -1, saved_errno }; +} + +static void print_prctl_result(const char *label, const prctl_probe_result &result) +{ + if (result.ok) { + std::printf("%s: ok, value=%d\n", label, result.value); + return; + } + print_errno_status(label, result.err); + std::printf("%s meaning: %s\n", label, prctl_failure_meaning(result.err)); +} + +static void print_kernel_version(void) +{ + struct utsname uts; + + if (uname(&uts) != 0) { + std::perror("uname"); + return; + } + std::printf("kernel: %s %s %s %s\n", + uts.sysname, uts.release, uts.version, uts.machine); +} + +static void print_slice_status_summary(bool has_slice_ctrl, bool flags_available, + const prctl_probe_result &get_result) +{ + bool flag_available = flags_available && + (g_registered_rseq_compat->flags & RSEQ_CS_FLAG_SLICE_EXT_AVAILABLE); + bool flag_enabled = flags_available && + (g_registered_rseq_compat->flags & RSEQ_CS_FLAG_SLICE_EXT_ENABLED); + bool prctl_enabled = get_result.ok && + (get_result.value & PR_RSEQ_SLICE_EXT_ENABLE); + + std::printf("status: rseq syscall registered: yes\n"); + std::printf("status: rseq extensible feature area: %s\n", + yes_no(g_rseq_feature_size > 20U)); + std::printf("status: rseq slice_ctrl field present: %s\n", + yes_no(has_slice_ctrl)); + std::printf("status: rseq slice extension available flag: %s\n", + flags_available ? set_clear(flag_available) : "unavailable"); + std::printf("status: rseq slice extension enabled flag: %s\n", + flags_available ? set_clear(flag_enabled) : "unavailable"); + std::printf("status: PR_RSEQ_SLICE_EXTENSION GET usable: %s\n", + yes_no(get_result.ok)); + + if (get_result.ok) { + std::printf("status: PR_RSEQ_SLICE_EXTENSION enabled: %s\n", + yes_no(prctl_enabled)); + std::printf("status: rseq slice extension availability: %s\n", + prctl_enabled || flag_available ? "available" : "available but disabled"); + return; + } + if (get_result.err == ENOTSUPP) { + std::printf("status: rseq slice extension availability: not supported by this kernel/arch/config\n"); + return; + } + if (get_result.err == EINVAL) { + std::printf("status: rseq slice extension availability: no accepted prctl API on this kernel\n"); + return; + } + std::printf("status: rseq slice extension availability: unknown\n"); +} + static void probe_slice_extension(void) { bool has_slice_ctrl = feature_present(offsetof(struct rseq_compat, slice_ctrl) + sizeof(g_registered_rseq_compat->slice_ctrl)); bool flags_available = feature_present(offsetof(struct rseq_compat, flags) + sizeof(g_registered_rseq_compat->flags)); + prctl_probe_result prctl_get = probe_prctl_get(); unsigned int cpu = 0; unsigned int node = 0; - std::printf("AT_RSEQ_FEATURE_SIZE: %u\n", g_rseq_feature_size); - std::printf("AT_RSEQ_ALIGN: %u\n", g_rseq_align); + print_kernel_version(); + std::printf("AT_RSEQ_FEATURE_SIZE raw: %lu\n", g_aux_rseq_feature_size); + std::printf("AT_RSEQ_ALIGN raw: %lu\n", g_aux_rseq_align); + std::printf("effective rseq feature size: %u\n", g_rseq_feature_size); + std::printf("effective rseq alignment: %u\n", g_rseq_align); std::printf("registered rseq size: %u\n", g_rseq_alloc_size); print_registration_source(); + if (&__rseq_size != nullptr) { + std::printf("libc __rseq_size=%u __rseq_offset=%td __rseq_flags=0x%x\n", + __rseq_size, __rseq_offset, __rseq_flags); + } std::printf("registered rseq addr: %p\n", static_cast(g_registered_rseq)); std::printf("struct rseq has slice_ctrl field available: %s\n", - has_slice_ctrl ? "yes" : "no"); + yes_no(has_slice_ctrl)); if (sys_getcpu(&cpu, &node) == 0) std::printf("getcpu(): cpu=%u node=%u\n", cpu, node); @@ -273,9 +414,9 @@ static void probe_slice_extension(void) if (flags_available) { std::printf("rseq flags=0x%x\n", g_registered_rseq_compat->flags); std::printf("slice ext available bit: %s\n", - (g_registered_rseq_compat->flags & RSEQ_CS_FLAG_SLICE_EXT_AVAILABLE) ? "set" : "clear"); + set_clear(g_registered_rseq_compat->flags & RSEQ_CS_FLAG_SLICE_EXT_AVAILABLE)); std::printf("slice ext enabled bit: %s\n", - (g_registered_rseq_compat->flags & RSEQ_CS_FLAG_SLICE_EXT_ENABLED) ? "set" : "clear"); + set_clear(g_registered_rseq_compat->flags & RSEQ_CS_FLAG_SLICE_EXT_ENABLED)); } if (has_slice_ctrl) { @@ -285,22 +426,19 @@ static void probe_slice_extension(void) g_registered_rseq_compat->slice_ctrl.all); } - errno = 0; - int rc = prctl(PR_RSEQ_SLICE_EXTENSION, PR_RSEQ_SLICE_EXTENSION_GET, 0UL, 0UL, 0UL); - int saved_errno = errno; - - if (rc >= 0) { - std::printf("prctl(GET) result=%d\n", rc); - std::printf("slice extension runtime availability: yes\n"); - std::printf("slice extension enabled for this thread: %s\n", - (rc & PR_RSEQ_SLICE_EXT_ENABLE) ? "yes" : "no"); - return; + print_prctl_result("prctl(PR_RSEQ_SLICE_EXTENSION, GET)", prctl_get); + if (prctl_get.ok) { + prctl_probe_result set_enable = probe_prctl_set(PR_RSEQ_SLICE_EXT_ENABLE); + print_prctl_result("prctl(PR_RSEQ_SLICE_EXTENSION, SET enable)", + set_enable); + prctl_probe_result after_enable = probe_prctl_get(); + print_prctl_result("prctl(PR_RSEQ_SLICE_EXTENSION, GET after enable)", + after_enable); + prctl_probe_result set_disable = probe_prctl_set(0UL); + print_prctl_result("prctl(PR_RSEQ_SLICE_EXTENSION, SET disable)", + set_disable); } - - std::printf("prctl(GET) failed: errno=%d (%s)\n", - saved_errno, errno_name(saved_errno)); - std::printf("slice extension runtime availability: %s\n", - saved_errno == EINVAL ? "no" : "unknown"); + print_slice_status_summary(has_slice_ctrl, flags_available, prctl_get); } int main()