Skip to content

Commit

Permalink
i#774 x64 reachability: extend reachability to out-of-line helper cal…
Browse files Browse the repository at this point in the history
…ls (#2564)

Applies DR_CLEANCALL_INDIRECT to out-of-line helper calls.  Removes the
hardcoded direct calls used for out-of-line clean call setup and cleanup
code and replaces with insert_reachable_cti().

Fixes vmcode_unreachable_pc() to avoid a wraparound issue.

Adds a test where a client generates a clean call into custom
non-code-cache memory.
  • Loading branch information
derekbruening authored Jul 25, 2017
1 parent 388fa09 commit 77899b0
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 32 deletions.
2 changes: 1 addition & 1 deletion core/arch/aarchxx/mangle.c
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,7 @@ insert_reachable_cti(dcontext_t *dcontext, instrlist_t *ilist, instr_t *where,

int
insert_out_of_line_context_switch(dcontext_t *dcontext, instrlist_t *ilist,
instr_t *instr, bool save)
instr_t *instr, bool save, byte *encode_pc)
{
# ifdef AARCH64
if (save) {
Expand Down
6 changes: 3 additions & 3 deletions core/arch/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -541,10 +541,10 @@ insert_get_mcontext_base(dcontext_t *dcontext, instrlist_t *ilist,
instr_t *where, reg_id_t reg);
uint
prepare_for_clean_call(dcontext_t *dcontext, clean_call_info_t *cci,
instrlist_t *ilist, instr_t *instr);
instrlist_t *ilist, instr_t *instr, byte *encode_pc);
void
cleanup_after_clean_call(dcontext_t *dcontext, clean_call_info_t *cci,
instrlist_t *ilist, instr_t *instr);
instrlist_t *ilist, instr_t *instr, byte *encode_pc);
void convert_to_near_rel(dcontext_t *dcontext, instr_t *instr);
instr_t *convert_to_near_rel_meta(dcontext_t *dcontext, instrlist_t *ilist,
instr_t *instr);
Expand All @@ -557,7 +557,7 @@ int find_syscall_num(dcontext_t *dcontext, instrlist_t *ilist, instr_t *instr);
/* in mangle.c but not exported to non-arch files */
int
insert_out_of_line_context_switch(dcontext_t *dcontext, instrlist_t *ilist,
instr_t *instr, bool save);
instr_t *instr, bool save, byte *encode_pc);
#ifdef X86
# ifdef UNIX
/* Mangle reference of fs/gs semgents, reg must not be used in the oldop. */
Expand Down
8 changes: 4 additions & 4 deletions core/arch/mangle_shared.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ insert_get_mcontext_base(dcontext_t *dcontext, instrlist_t *ilist,
#define NUM_EXTRA_SLOTS 2 /* pc, aflags */
uint
prepare_for_clean_call(dcontext_t *dcontext, clean_call_info_t *cci,
instrlist_t *ilist, instr_t *instr)
instrlist_t *ilist, instr_t *instr, byte *encode_pc)
{
uint dstack_offs = 0;

Expand Down Expand Up @@ -259,7 +259,7 @@ prepare_for_clean_call(dcontext_t *dcontext, clean_call_info_t *cci,
*/
if (cci->out_of_line_swap) {
dstack_offs +=
insert_out_of_line_context_switch(dcontext, ilist, instr, true);
insert_out_of_line_context_switch(dcontext, ilist, instr, true, encode_pc);
} else {
dstack_offs +=
insert_push_all_registers(dcontext, cci, ilist, instr, (uint)PAGE_SIZE,
Expand Down Expand Up @@ -311,7 +311,7 @@ prepare_for_clean_call(dcontext_t *dcontext, clean_call_info_t *cci,

void
cleanup_after_clean_call(dcontext_t *dcontext, clean_call_info_t *cci,
instrlist_t *ilist, instr_t *instr)
instrlist_t *ilist, instr_t *instr, byte *encode_pc)
{
if (cci == NULL)
cci = &default_clean_call_info;
Expand All @@ -337,7 +337,7 @@ cleanup_after_clean_call(dcontext_t *dcontext, clean_call_info_t *cci,

/* now restore everything */
if (cci->out_of_line_swap) {
insert_out_of_line_context_switch(dcontext, ilist, instr, false);
insert_out_of_line_context_switch(dcontext, ilist, instr, false, encode_pc);
} else {
/* XXX: add a cci field for optimizing this away if callee makes no calls */
insert_pop_all_registers(dcontext, cci, ilist, instr,
Expand Down
21 changes: 12 additions & 9 deletions core/arch/x86/mangle.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ remangle_short_rewrite(dcontext_t *dcontext,

int
insert_out_of_line_context_switch(dcontext_t *dcontext, instrlist_t *ilist,
instr_t *instr, bool save)
instr_t *instr, bool save, byte *encode_pc)
{
if (save) {
/* We adjust the stack so the return address will not be clobbered,
Expand All @@ -294,11 +294,13 @@ insert_out_of_line_context_switch(dcontext_t *dcontext, instrlist_t *ilist,
get_clean_call_temp_stack_size()),
OPSZ_lea)));
}
PRE(ilist, instr,
INSTR_CREATE_call
(dcontext, save ?
opnd_create_pc(get_clean_call_save(dcontext _IF_X64(GENCODE_X64))) :
opnd_create_pc(get_clean_call_restore(dcontext _IF_X64(GENCODE_X64)))));
/* We document to clients that we use r11 if we need an indirect call here. */
insert_reachable_cti(dcontext, ilist, instr, encode_pc,
save ?
get_clean_call_save(dcontext _IF_X64(GENCODE_X64)) :
get_clean_call_restore(dcontext _IF_X64(GENCODE_X64)),
false/*call*/, true/*returns*/,
false/*!precise*/, DR_REG_R11, NULL);
return get_clean_call_switch_stack_size();
}

Expand Down Expand Up @@ -901,7 +903,8 @@ insert_clean_call_with_arg_jmp_if_ret_true(dcontext_t *dcontext,
app_pc jmp_tag, instr_t *jmp_instr)
{
instr_t *false_popa, *jcc;
prepare_for_clean_call(dcontext, NULL, ilist, instr);
byte *encode_pc = vmcode_get_start();
prepare_for_clean_call(dcontext, NULL, ilist, instr, encode_pc);

dr_insert_call(dcontext, ilist, instr, callee, 1, OPND_CREATE_INT32(arg));

Expand All @@ -915,7 +918,7 @@ insert_clean_call_with_arg_jmp_if_ret_true(dcontext_t *dcontext,
/* if it falls through, then it's true, so restore and jmp to true tag
* passed in by caller
*/
cleanup_after_clean_call(dcontext, NULL, ilist, instr);
cleanup_after_clean_call(dcontext, NULL, ilist, instr, encode_pc);
if (jmp_instr == NULL) {
/* an exit cti, not a meta instr */
instrlist_preinsert
Expand All @@ -928,7 +931,7 @@ insert_clean_call_with_arg_jmp_if_ret_true(dcontext_t *dcontext,
/* otherwise (if returned false), just do standard popf and continue */
/* get 1st instr of cleanup path */
false_popa = instr_get_prev(instr);
cleanup_after_clean_call(dcontext, NULL, ilist, instr);
cleanup_after_clean_call(dcontext, NULL, ilist, instr, encode_pc);
false_popa = instr_get_next(false_popa);
instr_set_target(jcc, opnd_create_instr(false_popa));
}
Expand Down
16 changes: 14 additions & 2 deletions core/heap.c
Original file line number Diff line number Diff line change
Expand Up @@ -973,12 +973,24 @@ vmcode_get_end(void)
byte *
vmcode_unreachable_pc(void)
{
#ifdef X86_64
/* This is used to indicate something that is unreachable from *everything*
* for DR_CLEANCALL_INDIRECT, so ideally we want to not just provide an
* address that vmcode can't reach.
* We use a non-canonical address for x86_64.
*/
return (byte *)0x8000000100000000ULL;
#else
/* This is not really used for aarch* so we just go with vmcode reachability. */
ptr_uint_t start, end;
get_vmm_heap_bounds((byte **)&start, (byte **)&end);
if (start > INT_MAX)
return NULL;
else
return (byte *) PTR_UINT_MINUS_1;
else {
/* We do not use -1 to avoid wraparound from thinking it's reachable. */
return (byte *)end + INT_MAX + PAGE_SIZE;
}
#endif
}

bool
Expand Down
26 changes: 14 additions & 12 deletions core/lib/instrument.c
Original file line number Diff line number Diff line change
Expand Up @@ -5111,12 +5111,12 @@ dr_insert_call_noreturn(void *drcontext, instrlist_t *ilist, instr_t *where,
*/
static uint
prepare_for_call_ex(dcontext_t *dcontext, clean_call_info_t *cci,
instrlist_t *ilist, instr_t *where)
instrlist_t *ilist, instr_t *where, byte *encode_pc)
{
instr_t *in;
uint dstack_offs;
in = (where == NULL) ? instrlist_last(ilist) : instr_get_prev(where);
dstack_offs = prepare_for_clean_call(dcontext, cci, ilist, where);
dstack_offs = prepare_for_clean_call(dcontext, cci, ilist, where, encode_pc);
/* now go through and mark inserted instrs as meta */
if (in == NULL)
in = instrlist_first(ilist);
Expand All @@ -5132,7 +5132,8 @@ prepare_for_call_ex(dcontext_t *dcontext, clean_call_info_t *cci,
/* Internal utility routine for inserting context restore for a clean call. */
static void
cleanup_after_call_ex(dcontext_t *dcontext, clean_call_info_t *cci,
instrlist_t *ilist, instr_t *where, uint sizeof_param_area)
instrlist_t *ilist, instr_t *where, uint sizeof_param_area,
byte *encode_pc)
{
instr_t *in;
in = (where == NULL) ? instrlist_last(ilist) : instr_get_prev(where);
Expand All @@ -5145,7 +5146,7 @@ cleanup_after_call_ex(dcontext_t *dcontext, clean_call_info_t *cci,
XINST_CREATE_add(dcontext, opnd_create_reg(REG_XSP),
OPND_CREATE_INT8(sizeof_param_area)));
}
cleanup_after_clean_call(dcontext, cci, ilist, where);
cleanup_after_clean_call(dcontext, cci, ilist, where, encode_pc);
/* now go through and mark inserted instrs as meta */
if (in == NULL)
in = instrlist_first(ilist);
Expand Down Expand Up @@ -5245,7 +5246,11 @@ dr_insert_clean_call_ex_varg(void *drcontext, instrlist_t *ilist, instr_t *where
}
#endif
}
dstack_offs = prepare_for_call_ex(dcontext, &cci, ilist, where);
if (TEST(DR_CLEANCALL_INDIRECT, save_flags))
encode_pc = vmcode_unreachable_pc();
else
encode_pc = vmcode_get_start();
dstack_offs = prepare_for_call_ex(dcontext, &cci, ilist, where, encode_pc);
#ifdef X64
/* PR 218790: we assume that dr_prepare_for_call() leaves stack 16-byte
* aligned, which is what insert_meta_call_vargs requires. */
Expand Down Expand Up @@ -5276,10 +5281,6 @@ dr_insert_clean_call_ex_varg(void *drcontext, instrlist_t *ilist, instr_t *where
* flag will disappear and translation will fail.
*/
instrlist_set_our_mangling(ilist, true);
if (TEST(DR_CLEANCALL_INDIRECT, save_flags))
encode_pc = vmcode_unreachable_pc();
else
encode_pc = vmcode_get_start();
if (TEST(DR_CLEANCALL_RETURNS_TO_NATIVE, save_flags))
call_flags |= META_CALL_RETURNS_TO_NATIVE;
insert_meta_call_vargs(dcontext, ilist, where, call_flags,
Expand All @@ -5293,7 +5294,7 @@ dr_insert_clean_call_ex_varg(void *drcontext, instrlist_t *ilist, instr_t *where
MINSERT(ilist, where, XINST_CREATE_add(dcontext, opnd_create_reg(REG_XSP),
OPND_CREATE_INT32(buf_sz + pad)));
}
cleanup_after_call_ex(dcontext, &cci, ilist, where, 0);
cleanup_after_call_ex(dcontext, &cci, ilist, where, 0, encode_pc);
}

void
Expand Down Expand Up @@ -5345,7 +5346,8 @@ dr_prepare_for_call(void *drcontext, instrlist_t *ilist, instr_t *where)
CLIENT_ASSERT(drcontext != NULL, "dr_prepare_for_call: drcontext cannot be NULL");
CLIENT_ASSERT(drcontext != GLOBAL_DCONTEXT,
"dr_prepare_for_call: drcontext is invalid");
return prepare_for_call_ex((dcontext_t *)drcontext, NULL, ilist, where);
return prepare_for_call_ex((dcontext_t *)drcontext, NULL, ilist, where,
vmcode_get_start());
}

DR_API void
Expand All @@ -5356,7 +5358,7 @@ dr_cleanup_after_call(void *drcontext, instrlist_t *ilist, instr_t *where,
CLIENT_ASSERT(drcontext != GLOBAL_DCONTEXT,
"dr_cleanup_after_call: drcontext is invalid");
cleanup_after_call_ex((dcontext_t *)drcontext, NULL, ilist, where,
sizeof_param_area);
sizeof_param_area, vmcode_get_start());
}

#ifdef CLIENT_INTERFACE
Expand Down
3 changes: 2 additions & 1 deletion core/lib/instrument_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -5027,7 +5027,8 @@ typedef enum {
/** Skip saving any XMM or YMM registers that are never used as return values. */
DR_CLEANCALL_NOSAVE_XMM_NONRET = 0x0010,
/**
* Requests that an indirect call be used to ensure reachability.
* Requests that an indirect call be used to ensure reachability, both for
* reaching the callee and for any out-of-line helper routine calls.
* Only honored for 64-bit mode, where r11 will be used for the indirection.
*/
DR_CLEANCALL_INDIRECT = 0x0020,
Expand Down
4 changes: 4 additions & 0 deletions suite/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2270,6 +2270,10 @@ if (CLIENT_INTERFACE)
"" "-thread_private -cache_bb_unit_init 4K" "")
endif ()

if (X86) # Tests x86 reachability.
tobuild_ci(client.reachability client-interface/reachability.c "" "" "")
endif ()

if (X86) # FIXME i#1551, i#1569: fix bugs on ARM and AArch64
tobuild_ci(client.nudge_ex client-interface/nudge_ex.c "" "" "")
use_DynamoRIO_extension(client.nudge_ex.dll drmgr)
Expand Down
95 changes: 95 additions & 0 deletions suite/tests/client-interface/reachability.dll.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/* **********************************************************
* Copyright (c) 2017 Google, Inc. All rights reserved.
* **********************************************************/

/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Google, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/

#include "dr_api.h"

#define PRE instrlist_meta_preinsert

#define GENCODE_SIZE 4096

static app_pc gencode;
static bool used_gencode;

static void
clean_call(int arg)
{
dr_fprintf(STDERR, "inside clean call arg=%d\n", arg);
}

static dr_emit_flags_t
event_bb(void *dc, void *tag, instrlist_t *bb, bool for_trace, bool translating)
{
if (!used_gencode) { /* We assume a single-threaded app. */
instr_t *where = instrlist_first(bb);
instr_t *ret_label = INSTR_CREATE_label(dc);
dr_save_reg(dc, bb, where, DR_REG_XAX, SPILL_SLOT_1);
dr_save_reg(dc, bb, where, DR_REG_XDX, SPILL_SLOT_2);
PRE(bb, where, INSTR_CREATE_mov_imm(dc, opnd_create_reg(DR_REG_XAX),
opnd_create_instr(ret_label)));
instrlist_insert_mov_immed_ptrsz(dc, (ptr_int_t)gencode,
opnd_create_reg(DR_REG_XDX), bb, where,
NULL, NULL);
PRE(bb, where, INSTR_CREATE_jmp_ind(dc, opnd_create_reg(DR_REG_XDX)));
PRE(bb, where, ret_label);
dr_restore_reg(dc, bb, where, DR_REG_XDX, SPILL_SLOT_2);
dr_restore_reg(dc, bb, where, DR_REG_XAX, SPILL_SLOT_1);
used_gencode = true;
}
return DR_EMIT_DEFAULT;
}

static void
event_exit(void)
{
dr_nonheap_free(gencode, GENCODE_SIZE);
}

DR_EXPORT void
dr_init(client_id_t id)
{
/* Use some API routines in non-code-cache code to test reachability. */
void *dc = dr_get_current_drcontext();
instrlist_t *ilist = instrlist_create(dc);
dr_insert_clean_call_ex(dc, ilist, NULL, (void *)clean_call,
DR_CLEANCALL_ALWAYS_OUT_OF_LINE|DR_CLEANCALL_INDIRECT, 1,
/* Pass a magic value we'll check in the template */
OPND_CREATE_INT32(42));
/* Now return */
PRE(ilist, NULL, INSTR_CREATE_jmp_ind(dc, opnd_create_reg(DR_REG_XAX)));
gencode = dr_raw_mem_alloc(GENCODE_SIZE,
DR_MEMPROT_READ|DR_MEMPROT_WRITE|DR_MEMPROT_EXEC, NULL);
instrlist_encode(dc, ilist, gencode, false /*no relative jumps*/);
instrlist_clear_and_destroy(dc, ilist);

dr_register_bb_event(event_bb);
dr_register_exit_event(event_exit);
}
2 changes: 2 additions & 0 deletions suite/tests/client-interface/reachability.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
inside clean call arg=42
Hello, world!

0 comments on commit 77899b0

Please sign in to comment.