diff --git a/core/arch/mangle_shared.c b/core/arch/mangle_shared.c index d72cb8f917574da1f5a196f05c419da0d73520b4..1b547052c3d81ec665be505234cbfcafef932bd2 100644 --- a/core/arch/mangle_shared.c +++ b/core/arch/mangle_shared.c @@ -988,6 +988,7 @@ mangle_rseq_insert_native_sequence(dcontext_t *dcontext, instrlist_t *ilist, int i; for (i = 0; i < DR_NUM_GPR_REGS; i++) { if (reg_written[i]) { + /* XXX: Keep this consistent with instr_is_rseq_load() in translate.c. */ size_t offs = offsetof(dcontext_t, rseq_entry_state) + sizeof(reg_t) * i; PRE(ilist, insert_at, XINST_CREATE_load(dcontext, diff --git a/core/translate.c b/core/translate.c index 0c2991d3080f7ede5b6a53cf8d91de10703ca5aa..deb6c2726a1c912db0718782f514d644c24d88d4 100644 --- a/core/translate.c +++ b/core/translate.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2010-2019 Google, Inc. All rights reserved. + * Copyright (c) 2010-2020 Google, Inc. All rights reserved. * Copyright (c) 2000-2010 VMware, Inc. All rights reserved. * **********************************************************/ @@ -159,6 +159,31 @@ instr_is_seg_ref_load(dcontext_t *dcontext, instr_t *inst) # endif /* X86 */ return false; } + +static inline bool +instr_is_rseq_load(dcontext_t *dcontext, instr_t *inst) +{ + /* TODO i#2350: Add non-x86 support. */ +# if defined(LINUX) && defined(X86) + /* This won't fault but we don't want it marked as unsupported. */ + if (!instr_is_our_mangling(inst)) + return false; + /* XXX: Keep this consistent with mangle_rseq_* in mangle_shared.c. */ + if (instr_get_opcode(inst) == OP_mov_ld && opnd_is_reg(instr_get_dst(inst, 0)) && + opnd_is_base_disp(instr_get_src(inst, 0))) { + reg_id_t dst = opnd_get_reg(instr_get_dst(inst, 0)); + opnd_t memref = instr_get_src(inst, 0); + int disp = opnd_get_disp(memref); + if (reg_is_gpr(dst) && reg_is_pointer_sized(dst) && + opnd_get_index(memref) == DR_REG_NULL && + disp == + offsetof(dcontext_t, rseq_entry_state) + + sizeof(reg_t) * (dst - DR_REG_START_GPR)) + return true; + } +# endif + return false; +} #endif /* UNIX */ #ifdef ARM @@ -394,6 +419,8 @@ translate_walk_track(dcontext_t *tdcontext, instr_t *inst, translate_walk_t *wal /* nothing to do */ } else if (instr_is_seg_ref_load(tdcontext, inst)) { /* nothing to do */ + } else if (instr_is_rseq_load(tdcontext, inst)) { + /* nothing to do */ } #endif #ifdef ARM diff --git a/suite/tests/linux/rseq.c b/suite/tests/linux/rseq.c index 1405d611fbf237c56609fbc9c9f2aa63e7059f3c..a57a03cd7206970dafbec334750c8978df770487 100644 --- a/suite/tests/linux/rseq.c +++ b/suite/tests/linux/rseq.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2019 Google, Inc. All rights reserved. + * Copyright (c) 2019-2020 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -190,8 +190,6 @@ test_rseq_call(void) static void test_rseq_branches_once(bool force_restart, int *completions_out, int *restarts_out) { - /* We use static to avoid stack reference issues with our extra frame inside the asm. - */ __u32 id = RSEQ_CPU_ID_UNINITIALIZED; int completions = 0; int restarts = 0; @@ -271,6 +269,71 @@ test_rseq_branches(void) assert(completions == 1 && restarts > 0 && sigill_count == 1); } +/* Tests that DR handles a signal inside its native rseq copy. + * Any synchronous signal is going to pretty much never happen for real, since + * it would happen on the instrumentation execution and never make it to the + * native run, but an asynchronous signal could arrive. + * It's complicated to set up an asynchronous signal at the right spot, so + * we cheat and take advantage of DR not restoring XMM state to have different + * behavior in the two DR executions of the rseq code. + */ +static void +test_rseq_native_fault(void) +{ + int restarts = 0; + __asm__ __volatile__( + /* clang-format off */ /* (avoid indenting next few lines) */ + RSEQ_ADD_TABLE_ENTRY(fault, 2f, 3f, 4f) + /* clang-format on */ + + "6:\n\t" + /* Store the entry into the ptr. */ + "leaq rseq_cs_fault(%%rip), %%rax\n\t" + "movq %%rax, %[rseq_tls]\n\t" + "pxor %%xmm0, %%xmm0\n\t" + "mov $1,%%rcx\n\t" + "movq %%rcx, %%xmm1\n\t" + + /* Restartable sequence. */ + "2:\n\t" + /* Increase xmm0 every time. DR (currently) won't restore xmm inputs + * to rseq sequences, nor does it detect that it needs to. + */ + "paddq %%xmm1,%%xmm0\n\t" + "movq %%xmm0, %%rax\n\t" + /* Only raise the signal on the 2nd run == native run. */ + "cmp $2, %%rax\n\t" + "jne 11f\n\t" + /* Raise a signal on the native run. */ + "ud2a\n\t" + "11:\n\t" + "nop\n\t" + + /* Post-commit. */ + "3:\n\t" + "jmp 5f\n\t" + + /* Abort handler. */ + /* clang-format off */ /* (avoid indenting next few lines) */ + ".long " STRINGIFY(RSEQ_SIG) "\n\t" + "4:\n\t" + "addl $1, %[restarts]\n\t" + "jmp 2b\n\t" + + /* Clear the ptr. */ + "13:\n\t" + "12:\n\t" + "5:\n\t" + "movq $0, %[rseq_tls]\n\t" + /* clang-format on */ + + : [rseq_tls] "=m"(rseq_tls.rseq_cs), [restarts] "=m"(restarts) + : + : "rax", "rcx", "rdx", "xmm0", "xmm1", "memory"); + /* This is expected to fail on a native run where restarts will be 0. */ + assert(restarts > 0); +} + #ifdef RSEQ_TEST_ATTACH void * rseq_thread_loop(void *arg) @@ -356,6 +419,8 @@ main() test_rseq_call(); /* Test variations inside the sequence. */ test_rseq_branches(); + /* Test a fault in the native run. */ + test_rseq_native_fault(); /* Test a trace. */ int i; for (i = 0; i < 200; i++)