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#774 x64 reachability: extend reachability to out-of-line helper calls #2564

Merged
merged 5 commits into from
Jul 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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!