unsigned int rseq_flags;
static int rseq_ownership;
-static int rseq_reg_success; /* At least one rseq registration has succeded. */
/* Allocate a large area for the TLS. */
#define RSEQ_THREAD_AREA_ALLOC_SIZE 1024
return alloc_size;
}
+/*
+ * 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_kernel_feature_size(void)
+{
+ unsigned long auxv_rseq_feature_size, auxv_rseq_align;
+
+ auxv_rseq_align = getauxval(AT_RSEQ_ALIGN);
+ assert(!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE);
+
+ auxv_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE);
+ assert(!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE);
+ if (auxv_rseq_feature_size)
+ return auxv_rseq_feature_size;
+ else
+ return ORIG_RSEQ_FEATURE_SIZE;
+}
+
int rseq_register_current_thread(void)
{
int rc;
}
rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG);
if (rc) {
- if (RSEQ_READ_ONCE(rseq_reg_success)) {
+ /*
+ * After at least one thread has registered successfully
+ * (rseq_size > 0), the registration of other threads should
+ * never fail.
+ */
+ if (RSEQ_READ_ONCE(rseq_size) > 0) {
/* Incoherent success/failure within process. */
abort();
}
return -1;
}
assert(rseq_current_cpu_raw() >= 0);
- RSEQ_WRITE_ONCE(rseq_reg_success, 1);
+
+ /*
+ * The first thread to register sets the rseq_size to mimic the libc
+ * behavior.
+ */
+ if (RSEQ_READ_ONCE(rseq_size) == 0) {
+ RSEQ_WRITE_ONCE(rseq_size, get_rseq_kernel_feature_size());
+ }
+
return 0;
}
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_kernel_feature_size(void)
-{
- unsigned long auxv_rseq_feature_size, auxv_rseq_align;
-
- auxv_rseq_align = getauxval(AT_RSEQ_ALIGN);
- assert(!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE);
-
- auxv_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE);
- assert(!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE);
- if (auxv_rseq_feature_size)
- return auxv_rseq_feature_size;
- else
- return ORIG_RSEQ_FEATURE_SIZE;
-}
-
/*
* Initialize the public symbols for the rseq offset, size, feature size and
* flags prior to registering threads. If glibc owns the registration, get the
/* rseq flags are deprecated, always set to 0. */
rseq_flags = 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;
- goto unlock;
- }
- rseq_size = get_rseq_kernel_feature_size();
+ /*
+ * Set the size to 0 until at least one thread registers to mimic the
+ * libc behavior.
+ */
+ rseq_size = 0;
unlock:
pthread_mutex_unlock(&init_lock);
}
#include "tap.h"
+static void test_registered(void)
+{
+ struct rseq_abi *rseq_abi = rseq_get_abi();
+
+ ok(rseq_flags == 0, "rseq_flags after registration is 0 (%d)", rseq_flags);
+ ok(rseq_size >= 20, "rseq_size after registration is 20 or greater (%d)", rseq_size);
+ ok(rseq_offset != 0, "rseq_offset after registration is not 0 (%td)", rseq_offset);
+
+ ok((int32_t) rseq_abi->cpu_id >= 0,
+ "rseq->cpu_id after registration is 0 or greater (%d)",
+ (int32_t) rseq_abi->cpu_id);
+}
+
static void test_cpu_pointer(void)
{
cpu_set_t affinity, test_affinity;
pass("Registered current thread with rseq");
}
+ test_registered();
test_cpu_pointer();
if (rseq_unregister_current_thread()) {