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.h49
1 files changed, 38 insertions, 11 deletions
diff --git a/sysdeps/unix/sysv/linux/rseq-internal.h b/sysdeps/unix/sysv/linux/rseq-internal.h
index 00be15c..f89e784 100644
--- a/sysdeps/unix/sysv/linux/rseq-internal.h
+++ b/sysdeps/unix/sysv/linux/rseq-internal.h
@@ -26,6 +26,30 @@
#include <sys/rseq.h>
#include <ldsodefs.h>
#include <thread_pointer.h>
+#include <rseq-access.h>
+
+/* 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). */
@@ -47,10 +71,12 @@ 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.
+/* 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;
@@ -75,34 +101,35 @@ rseq_register_current_thread (struct pthread *self, bool do_rseq)
{
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.cpu_id_start, 0);
- THREAD_SETMEM (self, rseq_area.rseq_cs, 0);
- THREAD_SETMEM (self, rseq_area.flags, 0);
+ RSEQ_SETMEM (cpu_id, RSEQ_CPU_ID_UNINITIALIZED);
+ RSEQ_SETMEM (cpu_id_start, 0);
+ RSEQ_SETMEM (rseq_cs, 0);
+ RSEQ_SETMEM (flags, 0);
- 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 */