Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i#5431: Support glibc's rseq support #5711

Merged
merged 42 commits into from
Nov 18, 2022
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
0115daa
i#5431: Support GLIBC rseq support
abhinav92003 Oct 28, 2022
c6ac74d
Some fixes.
abhinav92003 Oct 28, 2022
d07698f
Fix api.rseq
abhinav92003 Oct 28, 2022
3b0805d
Add missing conditional compilation.
abhinav92003 Oct 29, 2022
f7eecc6
Merge branch 'master' into i5431-glibc-rseq
abhinav92003 Oct 29, 2022
a88e7d7
Cleanup.
abhinav92003 Oct 29, 2022
1b8a871
Add tmate
abhinav92003 Oct 29, 2022
0a0dfce
Revert "Add tmate"
abhinav92003 Oct 29, 2022
52e14e5
Cleanup.
abhinav92003 Oct 29, 2022
64cb131
Revert "Revert "Add tmate""
abhinav92003 Oct 29, 2022
cd069e5
Fix build
abhinav92003 Oct 29, 2022
51190be
Use dynamo_control_via_attach to detect attach mode
abhinav92003 Oct 31, 2022
ba77f10
Revert ubuntu-22 test filters.
abhinav92003 Oct 31, 2022
79d80f3
Remove the reached image entry hook, which is not very reliable.
abhinav92003 Oct 31, 2022
b80ec25
Remove spurious files.
abhinav92003 Oct 31, 2022
f2c3d7f
Merge branch 'master' into i5431-glibc-rseq
abhinav92003 Nov 1, 2022
25bb369
Remove hook in interp and adjust logic to locate rseq_cs entries.
abhinav92003 Nov 1, 2022
71d4881
Cleanup.
abhinav92003 Nov 1, 2022
782c811
Revert "Revert "Revert "Add tmate"""
abhinav92003 Nov 1, 2022
c418d74
Detect app's glibc instead of relying on the macro.
abhinav92003 Nov 1, 2022
8c2744a
Move check_glibc_rseq_support after lock
abhinav92003 Nov 1, 2022
1789ef4
Add ubuntu-22 test filters back.
abhinav92003 Nov 1, 2022
b57460b
Cleanup.
abhinav92003 Nov 1, 2022
599c8cc
Fix rseq registration in rseq_thread_loop
abhinav92003 Nov 1, 2022
9e059ab
Cleanup.
abhinav92003 Nov 2, 2022
1b69dad
Fix ENOSYS assertion in tests.
abhinav92003 Nov 2, 2022
bf8a606
Merge branch 'master' into i5431-glibc-rseq
abhinav92003 Nov 10, 2022
62e5908
Revert previous design since it doesn't work on fully static binaries.
abhinav92003 Nov 10, 2022
cd6d8c8
Alternate method of detecting glibc rseq.
abhinav92003 Nov 10, 2022
ea7b07a
Add glibc 2.35+ support for aarch64, and prevent hardcoding rseq tls …
abhinav92003 Nov 10, 2022
8a0bedd
Some cleanup.
abhinav92003 Nov 10, 2022
7c0ae88
Some cleanup.
abhinav92003 Nov 11, 2022
77d30f0
Fix log.
abhinav92003 Nov 11, 2022
320713d
Clarify registered rseq check.
abhinav92003 Nov 11, 2022
6bfd645
Check expected glibc rseq offset first.
abhinav92003 Nov 11, 2022
c092a1a
Review comments.
abhinav92003 Nov 11, 2022
9752a29
Add comment about AArch32.
abhinav92003 Nov 11, 2022
9489250
Update AArch32 comment.
abhinav92003 Nov 11, 2022
d0654ff
Merge branch 'master' into i5431-glibc-rseq
abhinav92003 Nov 11, 2022
949bd92
Improve logs.
abhinav92003 Nov 13, 2022
ddf88fc
Merge branch 'master' into i5431-glibc-rseq
abhinav92003 Nov 16, 2022
d2f3bbf
Merge branch 'master' into i5431-glibc-rseq
abhinav92003 Nov 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions core/dynamo.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ portable and to avoid frequency scaling."
# endif
#endif

#ifdef LINUX
# include "rseq_linux.h"
#endif
/* global thread-shared variables */
bool dynamo_initialized = false;
static bool dynamo_options_initialized = false;
Expand Down Expand Up @@ -693,6 +696,14 @@ dynamorio_app_init_part_two_finalize(void)
dynamo_vm_areas_unlock();
}

#ifdef LINUX
/* Rseq support should be initialized only after modules have been
* initialized too.
*/
if (!standalone_library)
d_r_rseq_init();
#endif

#ifdef ANNOTATIONS
annotation_init();
#endif
Expand Down Expand Up @@ -1168,6 +1179,10 @@ dynamo_shared_exit(thread_record_t *toexit /* must ==cur thread for Linux */
os_fast_exit();
os_slow_exit();
native_exec_exit(); /* before vm_areas_exit for using dynamo_areas */
#ifdef LINUX
if (!standalone_library)
d_r_rseq_exit();
#endif
vm_areas_exit();
perscache_slow_exit(); /* fast called in dynamo_process_exit_with_thread_info() */
modules_exit(); /* after aslr_exit() from os_slow_exit(),
Expand Down
8 changes: 0 additions & 8 deletions core/unix/os.c
Original file line number Diff line number Diff line change
Expand Up @@ -1031,10 +1031,6 @@ d_r_os_init(void)
*/
init_android_version();
#endif
#ifdef LINUX
if (!standalone_library)
d_r_rseq_init();
#endif
#ifdef MACOS64
tls_process_init();
#endif
Expand Down Expand Up @@ -1441,10 +1437,6 @@ os_slow_exit(void)
{
#ifdef MACOS64
tls_process_exit();
#endif
#ifdef LINUX
if (!standalone_library)
d_r_rseq_exit();
#endif
d_r_signal_exit();
memquery_exit();
Expand Down
108 changes: 104 additions & 4 deletions core/unix/rseq_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,26 @@ struct rseq {
} __attribute__((aligned(4 * sizeof(uint64))));
#define RSEQ_FLAG_UNREGISTER 1

/* Could be that the app is using glibc older than 2.35 (which did not provide
* rseq support), or no glibc at all.
*/
static volatile bool have_glibc_rseq;
/* __tls_init_tp in glibc/sysdeps/nptl/dl-tls_init_tp.c initializes __rseq_size
* and __rseq_offset. The former is > 0 iff glibc's rseq support is enabled
* (that is, not disabled by the user using GLIBC_TUNABLES=glibc.pthread.rseq=0
* or due to some issue). The latter is initialized regardless and is used by
* us to determine whether glibc has been initialized completely yet.
*/
static volatile uint glibc_rseq_size;
static volatile int glibc_rseq_offset;

vm_area_vector_t *d_r_rseq_areas;
DECLARE_CXTSWPROT_VAR(static mutex_t rseq_trigger_lock,
INIT_LOCK_FREE(rseq_trigger_lock));
static volatile bool rseq_enabled;

/* We require all threads to use the same TLS offset to point at struct rseq. */
static int rseq_tls_offset;
static volatile int rseq_tls_offset;

/* The signature is registered per thread, but we require all registrations
* to be the same.
Expand Down Expand Up @@ -151,6 +164,59 @@ rseq_cs_free(dcontext_t *dcontext, void *data)
} while (record != NULL);
}

void
rseq_check_glibc_support()
{
/* We have already determined glibc's rseq support. */
if (glibc_rseq_offset > 0)
return;
module_iterator_t *iter = module_iterator_start();
while (module_iterator_hasnext(iter)) {
module_area_t *ma = module_iterator_next(iter);
int *rseq_offset_addr =
(int *)dr_get_proc_address((module_handle_t)ma->start, "__rseq_offset");
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
if (rseq_offset_addr == NULL)
continue;
SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);
ATOMIC_1BYTE_WRITE(&have_glibc_rseq, true, false);
SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT);

/* Glibc is not completely initialized yet. */
if (*rseq_offset_addr == 0)
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
continue;
/* Verify that the offset has not changed. */
ASSERT(glibc_rseq_offset == 0 || glibc_rseq_offset == *rseq_offset_addr);
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);
ATOMIC_4BYTE_WRITE(&glibc_rseq_offset, *rseq_offset_addr, false);
SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT);

uint *rseq_size_addr =
(uint *)dr_get_proc_address((module_handle_t)ma->start, "__rseq_size");
ASSERT(rseq_size_addr != NULL);
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
/* Glibc's rseq support is disabled. */
if (*rseq_size_addr == 0) {
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
ASSERT(glibc_rseq_size == 0);
continue;
}
ASSERT(rseq_tls_offset == 0 || rseq_tls_offset == *rseq_offset_addr);
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);
ATOMIC_4BYTE_WRITE(&rseq_tls_offset, *rseq_offset_addr, false);
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
ATOMIC_4BYTE_WRITE(&glibc_rseq_size, *rseq_size_addr, false);
SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT);

DODEBUG({
byte *rseq_addr = get_app_segment_base(LIB_SEG_TLS) + glibc_rseq_offset;
int res =
dynamorio_syscall(SYS_rseq, 4, rseq_addr, sizeof(struct rseq), 0, 0);
ASSERT(res == -EPERM);
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
LOG(GLOBAL, LOG_LOADER, 2,
"Found struct rseq registered by glibc @ " PFX " at TLS offset %d\n",
rseq_addr, rseq_tls_offset);
});
}
module_iterator_stop(iter);
}

void
d_r_rseq_init(void)
{
Expand All @@ -161,6 +227,7 @@ d_r_rseq_init(void)
rseq_cs_table = generic_hash_create(GLOBAL_DCONTEXT, INIT_RSEQ_CS_TABLE_SIZE, 80,
HASHTABLE_SHARED | HASHTABLE_PERSISTENT,
rseq_cs_free _IF_DEBUG("rseq_cs table"));
rseq_check_glibc_support();
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
/* Enable rseq pre-attach for things like dr_prepopulate_cache(). */
if (rseq_is_registered_for_current_thread())
rseq_locate_rseq_regions();
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -172,6 +239,15 @@ d_r_rseq_exit(void)
generic_hash_destroy(GLOBAL_DCONTEXT, rseq_cs_table);
vmvector_delete_vector(GLOBAL_DCONTEXT, d_r_rseq_areas);
DELETE_LOCK(rseq_trigger_lock);
/* Better to reset and detect again for reattaches. */
SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
ATOMIC_4BYTE_WRITE(&rseq_tls_offset, 0, false);
if (have_glibc_rseq) {
ATOMIC_1BYTE_WRITE(&have_glibc_rseq, false, false);
ATOMIC_4BYTE_WRITE(&glibc_rseq_offset, 0, false);
ATOMIC_4BYTE_WRITE(&glibc_rseq_size, 0, false);
}
SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT);
}

void
Expand Down Expand Up @@ -229,7 +305,7 @@ static void
rseq_clear_tls_ptr(dcontext_t *dcontext)
{
ASSERT(rseq_tls_offset != 0);
byte *base = get_segment_base(LIB_SEG_TLS);
byte *base = get_app_segment_base(LIB_SEG_TLS);
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
struct rseq *app_rseq = (struct rseq *)(base + rseq_tls_offset);
/* We're directly writing this in the cache, so we do not bother with safe_read
* or safe_write here either. We already cannot handle rseq adversarial cases.
Expand Down Expand Up @@ -310,6 +386,11 @@ rseq_shared_fragment_flushtime_update(dcontext_t *dcontext)
bool
rseq_is_registered_for_current_thread(void)
{
/* If glibc's rseq support is enabled, a struct rseq is already
* registered for all threads.
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
*/
if (glibc_rseq_size > 0)
return true;
/* Unfortunately there's no way to query the current rseq struct.
* For 64-bit we can pass a kernel address and look for EFAULT
* vs EINVAL, but there is no kernel address for 32-bit.
Expand Down Expand Up @@ -468,7 +549,13 @@ rseq_process_elf_sections(module_area_t *ma, bool at_map,
* relocated and only need the offset if relocations have not yet been applied.
*/
ssize_t entry_offs = 0;
if (at_map || (DYNAMO_OPTION(early_inject) && !dr_api_entry && !dynamo_started))
bool adjust_entry_offs =
at_map || (DYNAMO_OPTION(early_inject) && !dr_api_entry && !dynamo_started);
if (have_glibc_rseq) {
/* We want to set entry_offs if glibc has not been initialized yet. */
adjust_entry_offs |= (glibc_rseq_offset == 0);
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
}
if (adjust_entry_offs)
entry_offs = load_offs;
for (i = 0; i < elf_hdr->e_shnum; i++) {
#define RSEQ_PTR_ARRAY_SEC_NAME "__rseq_cs_ptr_array"
Expand Down Expand Up @@ -627,6 +714,11 @@ rseq_process_module(module_area_t *ma, bool at_map)
static int
rseq_locate_tls_offset(void)
{
/* If glibc's rseq support is enabled, we already know the offset. */
if (glibc_rseq_size > 0) {
ASSERT(rseq_tls_offset > 0 && glibc_rseq_offset == rseq_tls_offset);
return rseq_tls_offset;
}
/* We assume (and document) that the loader's static TLS is used, so every thread
* has a consistent %fs:-offs address. Unfortunately, using a local copy of the
* rseq code for our non-instrumented execution requires us to locate the app's
Expand Down Expand Up @@ -735,7 +827,12 @@ rseq_locate_rseq_regions(void)
d_r_mutex_unlock(&rseq_trigger_lock);
return;
}

rseq_check_glibc_support();
/* If the app's glibc version supports rseq, then we should have already
* determined rseq_tls_offset, either by rseq_check_glibc_support or
* rseq_process_syscall (for non-attach cases).
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
*/
ASSERT(glibc_rseq_size == 0 || rseq_tls_offset > 0);
int offset = 0;
if (rseq_tls_offset == 0) {
/* Identify the TLS offset of this thread's struct rseq. */
Expand Down Expand Up @@ -768,6 +865,9 @@ rseq_locate_rseq_regions(void)
void
rseq_module_init(module_area_t *ma, bool at_map)
{
/* If !rseq_enabled, the module will simply be processed when we enable
* rseq.
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
*/
if (rseq_enabled) {
rseq_process_module(ma, at_map);
}
Expand Down
10 changes: 10 additions & 0 deletions suite/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5373,3 +5373,13 @@ if (BUILD_CLIENTS AND NOT ANDROID)
code_api|tool.drcacheoff.simple
PROPERTIES LABELS UBUNTU_22)
endif ()
if (LINUX AND X64 AND HAVE_RSEQ)
set_tests_properties(
code_api|api.rseq
code_api|linux.rseq_disable
code_api|linux.rseq_noarray
code_api|linux.rseq
code_api|linux.rseq_table
code_api|tool.drcacheoff.rseq
PROPERTIES LABELS UBUNTU_22)
endif ()
Loading