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;
/* 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.
*/
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);
}
/*
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))
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;
/* 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. */
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;
/* 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. */
/* 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;
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;
}
/* 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);
}
return;
rseq_offset = 0;
rseq_size = -1U;
- rseq_feature_size = -1U;
rseq_ownership = 0;
}