aboutsummaryrefslogtreecommitdiff
path: root/sysdeps/unix/sysv/linux/rseq-internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix/sysv/linux/rseq-internal.h')
-rw-r--r--sysdeps/unix/sysv/linux/rseq-internal.h101
1 files changed, 78 insertions, 23 deletions
diff --git a/sysdeps/unix/sysv/linux/rseq-internal.h b/sysdeps/unix/sysv/linux/rseq-internal.h
index 37a8f63..d2ab4cb 100644
--- a/sysdeps/unix/sysv/linux/rseq-internal.h
+++ b/sysdeps/unix/sysv/linux/rseq-internal.h
@@ -1,5 +1,5 @@
/* Restartable Sequences internal API. Linux implementation.
- Copyright (C) 2021-2024 Free Software Foundation, Inc.
+ Copyright (C) 2021-2025 Free Software Foundation, Inc.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -24,56 +24,111 @@
#include <stdbool.h>
#include <stdio.h>
#include <sys/rseq.h>
+#include <ldsodefs.h>
+#include <thread_pointer.h>
+#include <rseq-access.h>
-/* 32 is the initially required value for the area size. The
- actually used rseq size may be less (20 bytes initially). */
+/* rseq area registered with the kernel. Use a custom definition here to
+ isolate from the system provided header which could lack some fields of the
+ Extended ABI.
+
+ This is only used to get the field offsets and sizes, it should never be
+ used for direct object allocations.
+
+ Access to fields of the Extended ABI beyond the 20 bytes of the original ABI
+ (after 'flags') must be gated by a check of the feature size. */
+struct rseq_area
+{
+ /* Original ABI. */
+ uint32_t cpu_id_start;
+ uint32_t cpu_id;
+ uint64_t rseq_cs;
+ uint32_t flags;
+ /* Extended ABI. */
+ uint32_t node_id;
+ uint32_t mm_cid;
+ /* Flexible array member to discourage direct object allocations. */
+ char end[];
+};
+
+/* Minimum size of the rseq area allocation required by the syscall. The
+ actually used rseq feature size may be less (20 bytes initially). */
#define RSEQ_AREA_SIZE_INITIAL 32
+
+/* Minimum used feature size of the rseq area. */
#define RSEQ_AREA_SIZE_INITIAL_USED 20
-/* The variables are in .data.relro but are not yet write-protected. */
+/* Maximum currently used feature size of the rseq area. */
+#define RSEQ_AREA_SIZE_MAX_USED 28
+
+/* Minimum alignment of the rseq area. */
+#define RSEQ_MIN_ALIGN 32
+
+/* Alignment requirement of the rseq area.
+ Populated from the auxiliary vector with a minimum of '32'.
+ In .data.relro but not yet write-protected. */
+extern size_t _rseq_align attribute_hidden;
+
+/* Size of the active features in the rseq area.
+ Populated from the auxiliary vector with a minimum of '20'.
+ Set to '0' on registration failure of the main thread.
+ In .data.relro but not yet write-protected. */
extern unsigned int _rseq_size attribute_hidden;
+
+/* Offset from the thread pointer to the rseq area, always set to allow
+ checking the registration status by reading the 'cpu_id' field.
+ In .data.relro but not yet write-protected. */
extern ptrdiff_t _rseq_offset attribute_hidden;
+/* We want to use rtld_hidden_proto in order to call the internal aliases
+ of __rseq_size and __rseq_offset from ld.so. This avoids dynamic symbol
+ binding at run time for both variables. */
+rtld_hidden_proto (__rseq_size)
+rtld_hidden_proto (__rseq_offset)
+
+/* Returns a pointer to the current thread rseq area. */
+static inline struct rseq_area *
+RSEQ_SELF (void)
+{
+ return (struct rseq_area *) ((char *) __thread_pointer () + __rseq_offset);
+}
+
#ifdef RSEQ_SIG
static inline bool
rseq_register_current_thread (struct pthread *self, bool do_rseq)
{
if (do_rseq)
{
- unsigned int size;
-#if IS_IN (rtld)
- /* Use the hidden symbol in ld.so. */
- size = _rseq_size;
-#else
- size = __rseq_size;
-#endif
+ unsigned int size = __rseq_size;
+
+ /* The feature size can be smaller than the minimum rseq area size of 32
+ bytes accepted by the syscall, if this is the case, bump the size of
+ the registration to the minimum. The 'extra TLS' block is always at
+ least 32 bytes. */
if (size < RSEQ_AREA_SIZE_INITIAL)
- /* The initial implementation used only 20 bytes out of 32,
- but still expected size 32. */
size = RSEQ_AREA_SIZE_INITIAL;
- /* Initialize the rseq fields that are read by the kernel on
- registration, there is no guarantee that struct pthread is
- cleared on all architectures. */
- THREAD_SETMEM (self, rseq_area.cpu_id, RSEQ_CPU_ID_UNINITIALIZED);
- THREAD_SETMEM (self, rseq_area.rseq_cs, 0);
- THREAD_SETMEM (self, rseq_area.flags, 0);
+ /* Initialize the whole rseq area to zero prior to registration. */
+ memset (RSEQ_SELF (), 0, size);
+
+ /* Set the cpu_id field to RSEQ_CPU_ID_UNINITIALIZED, this is checked by
+ the kernel at registration when CONFIG_DEBUG_RSEQ is enabled. */
+ RSEQ_SETMEM (cpu_id, RSEQ_CPU_ID_UNINITIALIZED);
- int ret = INTERNAL_SYSCALL_CALL (rseq, &self->rseq_area,
- size, 0, RSEQ_SIG);
+ int ret = INTERNAL_SYSCALL_CALL (rseq, RSEQ_SELF (), size, 0, RSEQ_SIG);
if (!INTERNAL_SYSCALL_ERROR_P (ret))
return true;
}
/* When rseq is disabled by tunables or the registration fails, inform
userspace by setting 'cpu_id' to RSEQ_CPU_ID_REGISTRATION_FAILED. */
- THREAD_SETMEM (self, rseq_area.cpu_id, RSEQ_CPU_ID_REGISTRATION_FAILED);
+ RSEQ_SETMEM (cpu_id, RSEQ_CPU_ID_REGISTRATION_FAILED);
return false;
}
#else /* RSEQ_SIG */
static inline bool
rseq_register_current_thread (struct pthread *self, bool do_rseq)
{
- THREAD_SETMEM (self, rseq_area.cpu_id, RSEQ_CPU_ID_REGISTRATION_FAILED);
+ RSEQ_SETMEM (cpu_id, RSEQ_CPU_ID_REGISTRATION_FAILED);
return false;
}
#endif /* RSEQ_SIG */