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 35 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
5 changes: 2 additions & 3 deletions core/unix/os.c
Original file line number Diff line number Diff line change
Expand Up @@ -9371,7 +9371,6 @@ post_system_call(dcontext_t *dcontext)
/* Lazy rseq handling. */
if (success) {
rseq_process_syscall(dcontext);
rseq_locate_rseq_regions();
}
break;
#endif
Expand Down Expand Up @@ -10732,7 +10731,7 @@ os_take_over_all_unknown_threads(dcontext_t *dcontext)
#ifdef LINUX
/* Check this thread for rseq in between setup and start. */
if (rseq_is_registered_for_current_thread())
rseq_locate_rseq_regions();
rseq_locate_rseq_regions(false);
#endif

/* Find tids for which we have no thread record, meaning they are not under
Expand Down Expand Up @@ -10960,7 +10959,7 @@ os_thread_take_over(priv_mcontext_t *mc, kernel_sigset_t *sigset)
* regions as rseq when the rseq syscall is never set up.
*/
if (rseq_is_registered_for_current_thread()) {
rseq_locate_rseq_regions();
rseq_locate_rseq_regions(false);
rseq_thread_attach(dcontext);
}
#endif
Expand Down
131 changes: 92 additions & 39 deletions core/unix/rseq_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ DECLARE_CXTSWPROT_VAR(static mutex_t rseq_trigger_lock,
INIT_LOCK_FREE(rseq_trigger_lock));
static volatile bool rseq_enabled;

/* The struct rseq registered by glibc is present in the struct pthread.
* As of glibc 2.35, it is present at the following offset from the app
* lib seg base. We check these offsets first and then fall back to a
* wider search. The linux.rseq test helps detect changes in these
* offsets in future glibc versions.
*/
#ifdef X86
# define GLIBC_RSEQ_OFFSET 2464
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
#else
# define GLIBC_RSEQ_OFFSET -32
#endif

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

Expand Down Expand Up @@ -163,7 +175,7 @@ d_r_rseq_init(void)
rseq_cs_free _IF_DEBUG("rseq_cs table"));
/* Enable rseq pre-attach for things like dr_prepopulate_cache(). */
if (rseq_is_registered_for_current_thread())
rseq_locate_rseq_regions();
rseq_locate_rseq_regions(false);
}

void
Expand Down Expand Up @@ -229,7 +241,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 @@ -545,7 +557,7 @@ rseq_process_elf_sections(module_area_t *ma, bool at_map,

/* Returns whether successfully searched for rseq data (not whether found rseq data). */
static bool
rseq_process_module(module_area_t *ma, bool at_map)
rseq_process_module(module_area_t *ma, bool at_map, bool saw_glibc_rseq_reg)
{
bool res = false;
ASSERT(is_elf_so_header(ma->start, ma->end - ma->start));
Expand Down Expand Up @@ -596,7 +608,11 @@ rseq_process_module(module_area_t *ma, bool at_map)
goto rseq_process_module_cleanup;
strtab = (char *)(str_map + sec_hdr[elf_hdr->e_shstrndx].sh_offset - offs);
}
rseq_process_elf_sections(ma, at_map, sec_hdr, strtab, load_offs);
/* When saw_glibc_rseq_reg is set, we are still at glibc init, and ld has not
* relocated the executable yet.
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
*/
rseq_process_elf_sections(ma, at_map || saw_glibc_rseq_reg, sec_hdr, strtab,
load_offs);
res = true;
rseq_process_module_cleanup:
if (str_size != 0)
Expand All @@ -621,6 +637,31 @@ rseq_process_module(module_area_t *ma, bool at_map)
return res;
}

static bool
try_struct_rseq(void *try_addr)
{
static const int RSEQ_RARE_SIGNATURE = 42;
int res = dynamorio_syscall(SYS_rseq, 4, try_addr, sizeof(struct rseq),
RSEQ_FLAG_UNREGISTER, RSEQ_RARE_SIGNATURE);
LOG(GLOBAL, LOG_LOADER, 3, "Tried rseq @ " PFX " => %d\n", try_addr, res);
if (res == -EINVAL) /* Our struct != registered struct. */
return false;
/* We expect -EPERM on a signature mismatch. On the small chance the app
* actually used 42 for its signature we'll have to re-register it.
*/
if (res == 0) {
int res = dynamorio_syscall(SYS_rseq, 4, try_addr, sizeof(struct rseq), 0,
RSEQ_RARE_SIGNATURE);
ASSERT(res == 0);
res = -EPERM;
}
if (res == -EPERM) {
/* Found it! */
return true;
}
return false;
}

/* If we did not observe the app invoke SYS_rseq (because we attached mid-run)
* we must search for its TLS location.
*/
Expand All @@ -638,6 +679,19 @@ rseq_locate_tls_offset(void)
*/
int offset = 0;
byte *addr = get_app_segment_base(LIB_SEG_TLS);
if (addr > 0) {
byte *try_glibc_addr = addr + GLIBC_RSEQ_OFFSET;
if (try_struct_rseq(try_glibc_addr)) {
LOG(GLOBAL, LOG_LOADER, 2,
"Found glibc struct rseq @ " PFX " for thread => %s:%s0x%x\n",
try_glibc_addr, get_register_name(LIB_SEG_TLS),
(GLIBC_RSEQ_OFFSET < 0 ? "-" : ""), abs(GLIBC_RSEQ_OFFSET));
return GLIBC_RSEQ_OFFSET;
}
}
/* Either the app's glibc does not have rseq support (old glibc or disabled by app)
* or the offset of glibc's struct rseq has changed. We do a wider search now.
*/
byte *seg_bottom;
size_t seg_size;
if (addr > 0 && get_memory_info(addr, &seg_bottom, &seg_size, NULL)) {
Expand All @@ -646,41 +700,31 @@ rseq_locate_tls_offset(void)
/* struct rseq_cs is aligned to 32. */
int alignment = __alignof(struct rseq_cs);
int i;
/* For x86, static TLS is at a negative offset from the app library segment
* base, while for aarchxx it is positive.
/* When rseq support is enabled in glibc 2.35+, the glibc-registered struct rseq
* is present in the struct pthread, which is at a positive offset from the
* app library segment base on x86, and negative on aarchxx. However, in the
* absence of rseq support from glibc, the app manually registers its own
* struct rseq which is present in static TLS, which is at a negative offset
* from the app library segment base on x86, and positive on aarchxx.
*/
for (i = 0; IF_X86_ELSE(addr - i * alignment >= seg_bottom,
addr + i * alignment < seg_bottom + seg_size);
i++) {
byte *try_addr = IF_X86_ELSE(addr - i * alignment, addr + i * alignment);
ASSERT(try_addr >= seg_bottom); /* For loop guarantees this. */
/* Our strategy is to check all of the aligned static TLS addresses to
* find the registered one. Our caller is not supposed to call here
* until the app has registered the current thread.
ASSERT(seg_bottom <= addr && addr < seg_bottom + seg_size);
for (i = (seg_bottom - addr) / alignment;
addr + i * alignment < seg_bottom + seg_size; ++i) {
byte *try_addr = addr + i * alignment;
ASSERT(seg_bottom <= try_addr &&
try_addr < seg_bottom + seg_size); /* For loop guarantees this. */
/* Our strategy is to check all of the aligned addresses to find the
* registered one. Our caller is not supposed to call here until the app
* has registered the current thread (either manually or using glibc).
*/
static const int RSEQ_RARE_SIGNATURE = 42;
int res = dynamorio_syscall(SYS_rseq, 4, try_addr, sizeof(struct rseq),
RSEQ_FLAG_UNREGISTER, RSEQ_RARE_SIGNATURE);
LOG(GLOBAL, LOG_LOADER, 3, "Tried rseq @ " PFX " => %d\n", try_addr, res);
if (res == -EINVAL) /* Our struct != registered struct. */
continue;
/* We expect -EPERM on a signature mismatch. On the small chance the app
* actually used 42 for its signature we'll have to re-register it.
*/
if (res == 0) {
int res = dynamorio_syscall(SYS_rseq, 4, try_addr, sizeof(struct rseq), 0,
RSEQ_RARE_SIGNATURE);
ASSERT(res == 0);
res = -EPERM;
}
if (res == -EPERM) {
/* Found it! */
if (try_struct_rseq(try_addr)) {
LOG(GLOBAL, LOG_LOADER, 2,
"Found struct rseq @ " PFX " for thread => %s:%s0x%x\n", try_addr,
get_register_name(LIB_SEG_TLS), IF_X86_ELSE("-", ""), i * alignment);
offset = IF_X86_ELSE(-i * alignment, i * alignment);
get_register_name(LIB_SEG_TLS), (i < 0 ? "-" : ""),
abs(i) * alignment);
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
offset = i * alignment;
break;
}
break;
}
}
return offset;
Expand All @@ -692,17 +736,20 @@ rseq_process_syscall(dcontext_t *dcontext)
byte *seg_base = get_app_segment_base(LIB_SEG_TLS);
byte *app_addr = (byte *)dcontext->sys_param0;
bool constant_offset = false;
bool first_rseq_registration = false;
if (rseq_tls_offset == 0) {
SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);
int offset = app_addr - seg_base;
/* To handle races here, we use an atomic_exchange. */
int prior = atomic_exchange_int(&rseq_tls_offset, offset);
SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT);
if (prior == 0)
first_rseq_registration = true;
constant_offset = (prior == 0 || prior == offset);
LOG(GLOBAL, LOG_LOADER, 2,
"Observed struct rseq @ " PFX " for thread => %s:%s0x%x\n", app_addr,
get_register_name(LIB_SEG_TLS), IF_X86_ELSE("-", ""),
IF_X86_ELSE(-rseq_tls_offset, rseq_tls_offset));
get_register_name(LIB_SEG_TLS), (rseq_tls_offset < 0 ? "-" : ""),
abs(rseq_tls_offset));
} else
constant_offset = (seg_base + rseq_tls_offset == app_addr);
if (!constant_offset) {
Expand All @@ -711,6 +758,12 @@ rseq_process_syscall(dcontext_t *dcontext)
"struct rseq is not always in static thread-local storage");
ASSERT_NOT_REACHED();
}
/* The struct rseq registered by glibc 2.35+ is inside struct pthread, which is
* at a positive offset from thread pointer on x86 and negative offset on AArch64,
* both unlike the static TLS used by manual app registration.
*/
rseq_locate_rseq_regions(first_rseq_registration &&
IF_X86_ELSE(rseq_tls_offset > 0, rseq_tls_offset < 0));
}

/* Restartable sequence region identification.
Expand All @@ -723,7 +776,7 @@ rseq_process_syscall(dcontext_t *dcontext)
* the app has registered the current thread for rseq.
*/
void
rseq_locate_rseq_regions(void)
rseq_locate_rseq_regions(bool saw_glibc_rseq_reg)
{
if (rseq_enabled)
return;
Expand Down Expand Up @@ -759,7 +812,7 @@ rseq_locate_rseq_regions(void)
module_iterator_t *iter = module_iterator_start();
while (module_iterator_hasnext(iter)) {
module_area_t *ma = module_iterator_next(iter);
rseq_process_module(ma, false /*!at_map*/);
rseq_process_module(ma, false /*!at_map*/, saw_glibc_rseq_reg);
}
module_iterator_stop(iter);
d_r_mutex_unlock(&rseq_trigger_lock);
Expand All @@ -769,7 +822,7 @@ void
rseq_module_init(module_area_t *ma, bool at_map)
{
if (rseq_enabled) {
rseq_process_module(ma, at_map);
rseq_process_module(ma, at_map, false);
}
}

Expand Down
2 changes: 1 addition & 1 deletion core/unix/rseq_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ bool
rseq_is_registered_for_current_thread(void);

void
rseq_locate_rseq_regions(void);
rseq_locate_rseq_regions(bool);
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved

void
rseq_module_init(module_area_t *ma, bool 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 @@ -5375,3 +5375,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