Skip to content

Commit

Permalink
i#2283: Adds faulty redzones (#2130)
Browse files Browse the repository at this point in the history
Introduces a new option that renders redzones as faulty for umbra_32. When an access, i.e., a read or write, is made on a redzone, a fault occurs.

This is useful for detecting cross-boundary accesses without the need to perform explicit checks.
  • Loading branch information
johnfxgalea authored Jun 29, 2020
1 parent 7d8554d commit 3ac552c
Show file tree
Hide file tree
Showing 4 changed files with 363 additions and 9 deletions.
10 changes: 10 additions & 0 deletions tests/framework/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,16 @@ use_DynamoRIO_extension(umbra_test_insert_app_to_shadow.client drutil)
target_include_directories(umbra_test_insert_app_to_shadow.client PRIVATE
${CMAKE_CURRENT_SOURCE_DIR})

# Faulty redzones are only available on 32-bit.
if (NOT X64)
add_drmf_test(umbra_client_faulty_redzone umbra_app
umbra_client_faulty_redzone.c umbra "" ".*TEST PASSED")
use_DynamoRIO_extension(umbra_client_faulty_redzone.client drreg)
use_DynamoRIO_extension(umbra_client_faulty_redzone.client drutil)
target_include_directories(umbra_client_faulty_redzone.client PRIVATE
${CMAKE_CURRENT_SOURCE_DIR})
endif()

add_drmf_test(umbra_test_consistency umbra_app
umbra_client_consistency.c umbra "" ".*TEST PASSED")
use_DynamoRIO_extension(umbra_test_consistency.client drreg)
Expand Down
268 changes: 268 additions & 0 deletions tests/framework/umbra_client_faulty_redzone.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
/* **************************************************************
* Copyright (c) 2020 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.
*/

/* Tests umbra's faulty redzone option. */

#include <signal.h>
#include <string.h>

#include "dr_api.h"
#include "drmgr.h"
#include "drreg.h"
#include "drutil.h"
#include "umbra.h"

#define TEST(mask, var) (((mask) & (var)) != 0)

static umbra_map_t *umbra_map;
/* Denotes whether redzone faults were ever handled during the run. */
static bool redzone_fault = false;

static dr_emit_flags_t
event_app_instruction(void *drcontext, void *tag, instrlist_t *ilist, instr_t *where,
bool for_trace, bool translating, void *user_data);

#ifdef WINDOWS
static bool
event_exception_instrumentation(void *drcontext, dr_exception_t *excpt);
#else
static dr_signal_action_t
event_signal_instrumentation(void *drcontext, dr_siginfo_t *info);
#endif

static void
exit_event(void);

DR_EXPORT void
dr_client_main(client_id_t id, int argc, const char *argv[])
{
drreg_options_t ops = { sizeof(ops), 4, true };
umbra_map_options_t umbra_map_ops;

drmgr_init();
drreg_init(&ops);

memset(&umbra_map_ops, 0, sizeof(umbra_map_ops));
umbra_map_ops.scale = UMBRA_MAP_SCALE_DOWN_4X;
umbra_map_ops.flags =
UMBRA_MAP_CREATE_SHADOW_ON_TOUCH | UMBRA_MAP_SHADOW_SHARED_READONLY;
umbra_map_ops.default_value = 0;
umbra_map_ops.default_value_size = 1;
/* Enable redzones and make them faulty. */
umbra_map_ops.make_redzone_faulty = true;
if (umbra_init(id) != DRMF_SUCCESS)
DR_ASSERT_MSG(false, "fail to init umbra");
if (umbra_create_mapping(&umbra_map_ops, &umbra_map) != DRMF_SUCCESS)
DR_ASSERT_MSG(false, "fail to create shadow memory mapping");
drmgr_register_bb_instrumentation_event(NULL, event_app_instruction, NULL);
#ifdef WINDOWS
drmgr_register_exception_event(event_exception_instrumentation);
#else
drmgr_register_signal_event(event_signal_instrumentation);
#endif
dr_register_exit_event(exit_event);
}

static void
instrument_mem(void *drcontext, instrlist_t *ilist, instr_t *where, opnd_t ref)
{
reg_id_t regaddr;
reg_id_t scratch;
bool ok;

#ifdef X86
drvector_t allowed;
drreg_init_and_fill_vector(&allowed, false);
drreg_set_vector_entry(&allowed, DR_REG_XCX, true);
#endif

if (drreg_reserve_aflags(drcontext, ilist, where) != DRREG_SUCCESS ||
drreg_reserve_register(drcontext, ilist, where, IF_X86_ELSE(&allowed, NULL),
&scratch) != DRREG_SUCCESS ||
drreg_reserve_register(drcontext, ilist, where, NULL, &regaddr) !=
DRREG_SUCCESS) {
DR_ASSERT(false); /* Can't recover. */
return;
}
#ifdef X86
drvector_delete(&allowed);
#endif

ok = drutil_insert_get_mem_addr(drcontext, ilist, where, ref, regaddr, scratch);
DR_ASSERT(ok);
/* Save the app address to a well-known spill slot, so that the fault handler
* can recover if no shadow memory was installed yet.
*/
dr_save_reg(drcontext, ilist, where, regaddr, SPILL_SLOT_2);

if (umbra_insert_app_to_shadow(drcontext, umbra_map, ilist, where, regaddr, &scratch,
1) != DRMF_SUCCESS) {
DR_ASSERT(false);
}

/* Use a displacement of a page size to access ahead and try to hit a faulty
* redzone.
*/
instrlist_meta_preinsert(
ilist, where,
INSTR_XL8(XINST_CREATE_store_1byte(
drcontext,
OPND_CREATE_MEM8(regaddr, dr_page_size() /* enter redzone */),
opnd_create_reg(reg_resize_to_opsz(scratch, OPSZ_1))),
instr_get_app_pc(where)));

if (drreg_unreserve_register(drcontext, ilist, where, regaddr) != DRREG_SUCCESS ||
drreg_unreserve_register(drcontext, ilist, where, scratch) != DRREG_SUCCESS ||
drreg_unreserve_aflags(drcontext, ilist, where) != DRREG_SUCCESS)
DR_ASSERT(false);
}

static dr_emit_flags_t
event_app_instruction(void *drcontext, void *tag, instrlist_t *ilist, instr_t *where,
bool for_trace, bool translating, void *user_data)
{
int i;
for (i = 0; i < instr_num_srcs(where); i++) {
if (opnd_is_memory_reference(instr_get_src(where, i)))
instrument_mem(drcontext, ilist, where, instr_get_src(where, i));
}
for (i = 0; i < instr_num_dsts(where); i++) {
if (opnd_is_memory_reference(instr_get_dst(where, i)))
instrument_mem(drcontext, ilist, where, instr_get_dst(where, i));
}

return DR_EMIT_DEFAULT;
}

static void
exit_event(void)
{
if (umbra_destroy_mapping(umbra_map) != DRMF_SUCCESS)
DR_ASSERT(false);

DR_ASSERT_MSG(redzone_fault, "No redzone faults have been handled");

umbra_exit();
drreg_exit();
drmgr_exit();
}

static bool
handle_special_shadow_fault(void *drcontext, dr_mcontext_t *raw_mc, app_pc app_shadow)
{
umbra_shadow_memory_type_t shadow_type;
app_pc app_target;
instr_t inst;
reg_id_t reg;
bool is_page_disp;

instr_init(drcontext, &inst);
decode(drcontext, raw_mc->pc, &inst);
is_page_disp = opnd_get_disp(instr_get_dst(&inst, 0)) == dr_page_size();
reg = opnd_get_base(instr_get_dst(&inst, 0));
instr_free(drcontext, &inst);

if (umbra_get_shadow_memory_type(umbra_map, app_shadow, &shadow_type) !=
DRMF_SUCCESS) {
DR_ASSERT(false);
return true;
}

if (!TEST(UMBRA_SHADOW_MEMORY_TYPE_SHARED, shadow_type) &&
!TEST(UMBRA_SHADOW_MEMORY_TYPE_REDZONE, shadow_type) && !is_page_disp) {
return true;
} else if (!TEST(UMBRA_SHADOW_MEMORY_TYPE_SHARED, shadow_type) &&
!TEST(UMBRA_SHADOW_MEMORY_TYPE_REDZONE, shadow_type) && is_page_disp) {
/* Something weird has happened. Unlikely that a fault with a page size
* displacement is triggered not due to special or redzone regions.
*/
DR_ASSERT(false);
return true;
} else if ((!TEST(UMBRA_SHADOW_MEMORY_TYPE_SHARED, shadow_type) ||
TEST(UMBRA_SHADOW_MEMORY_TYPE_REDZONE, shadow_type)) &&
!is_page_disp) {
/* Something weird has happened. Unlikely that a fault
* is triggered due to special or redzone regions where no page size
* displacement is present.
*/
DR_ASSERT(false);
return true;
}

if (TEST(UMBRA_SHADOW_MEMORY_TYPE_REDZONE, shadow_type)) {
redzone_fault = true;
/* Fetch the base and cancel out the displacement so that we do
* not hit the redzone again.
*/
app_pc base_addr = (app_pc)(reg_t)reg_get_value(reg, raw_mc);
app_shadow = base_addr - dr_page_size();
} else {
/* This fault does not concern redzone handling. Instead, we need to create
* shadow memory on demand.
*/
DR_ASSERT(shadow_type == UMBRA_SHADOW_MEMORY_TYPE_SHARED);

app_target = (app_pc)dr_read_saved_reg(drcontext, SPILL_SLOT_2);
if (umbra_replace_shared_shadow_memory(umbra_map, app_target, &app_shadow) !=
DRMF_SUCCESS) {
DR_ASSERT(false);
return true;
}
}

reg_set_value(reg, raw_mc, (reg_t)app_shadow);

return false;
}

#ifdef WINDOWS
bool
event_exception_instrumentation(void *drcontext, dr_exception_t *excpt)
{
if (excpt->record->ExceptionCode != STATUS_ACCESS_VIOLATION)
return true;
return handle_special_shadow_fault(drcontext, excpt->raw_mcontext,
(byte *)excpt->record->ExceptionInformation[1]);
}
#else
static dr_signal_action_t
event_signal_instrumentation(void *drcontext, dr_siginfo_t *info)
{
if (info->sig != SIGSEGV && info->sig != SIGBUS)
return DR_SIGNAL_DELIVER;
DR_ASSERT(info->raw_mcontext_valid);
return handle_special_shadow_fault(drcontext, info->raw_mcontext,
info->access_address)
? DR_SIGNAL_DELIVER
: DR_SIGNAL_SUPPRESS;
}
#endif
16 changes: 16 additions & 0 deletions umbra/umbra.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,22 @@ typedef struct _umbra_map_options_t {
ptr_uint_t redzone_value;
/** The redzone value size, only 1 is supported now */
size_t redzone_value_size;
/**
* Set to true to render redzones as faulty, i.e., access (both read and write)
* to a redzone causes a fault.
*
* If a cross block access occurs, a fault is triggered. This is an
* optimisation, because rather than requiring the user to perform explicit
* checks on redzone access, a fault will indicate the case. The user may
* then take further action by defining a fault handler, similar to
* that done for shared block access (if enabled).
*
* Overrides redzone data specified in the struct, including redzone_size.
* With this option enabled, the fields: #redzone_size, #redzone_value and
* #redzone_value_size, are not considered. Redzone size is set to a page size
* by default to set appropriate access permissions.
*/
bool make_redzone_faulty;
#endif

/** Application memory creation callback. */
Expand Down
Loading

0 comments on commit 3ac552c

Please sign in to comment.