silently filter access violations in asan's shadow address space

This commit is contained in:
Ryan Fleury
2024-01-19 17:47:56 -08:00
parent 123c3104a5
commit 42993b3fb0
6 changed files with 232 additions and 8 deletions
+201
View File
@@ -1110,6 +1110,146 @@ ctrl_tls_root_vaddr_from_thread(CTRL_MachineID machine_id, CTRL_Handle thread)
return result;
}
//- rjf: process * vaddr -> module
internal CTRL_Handle
ctrl_module_from_process_vaddr(CTRL_MachineID machine_id, CTRL_Handle process, U64 vaddr)
{
CTRL_Handle handle = {0};
{
Temp scratch = scratch_begin(0, 0);
DEMON_HandleArray modules = demon_modules_from_process(scratch.arena, ctrl_demon_handle_from_ctrl(process));
for(U64 idx = 0; idx < modules.count; idx += 1)
{
DEMON_Handle m = modules.handles[idx];
Rng1U64 m_vaddr_rng = demon_vaddr_range_from_module(m);
if(contains_1u64(m_vaddr_rng, vaddr))
{
handle = ctrl_handle_from_demon(m);
break;
}
}
scratch_end(scratch);
}
return handle;
}
//- rjf: unwinding
internal CTRL_Unwind
ctrl_unwind_from_process_thread(Arena *arena, CTRL_MachineID machine_id, CTRL_Handle process, CTRL_Handle thread)
{
ProfBeginFunction();
Temp scratch = scratch_begin(&arena, 1);
DBGI_Scope *scope = dbgi_scope_open();
Architecture arch = demon_arch_from_object(ctrl_demon_handle_from_ctrl(thread));
U64 arch_reg_block_size = regs_block_size_from_architecture(arch);
CTRL_Unwind unwind = {0};
unwind.error = 1;
switch(arch)
{
default:{}break;
case Architecture_x64:
{
// rjf: grab initial register block
void *regs_block = push_array(scratch.arena, U8, arch_reg_block_size);
B32 regs_block_good = 0;
{
void *regs_raw = ctrl_reg_block_from_thread(machine_id, thread);
if(regs_raw != 0)
{
MemoryCopy(regs_block, regs_raw, arch_reg_block_size);
regs_block_good = 1;
}
}
// rjf: grab initial memory view
B32 stack_memview_good = 0;
UNW_MemView stack_memview = {0};
if(regs_block_good)
{
U64 stack_base_unrounded = demon_stack_base_vaddr_from_thread(ctrl_demon_handle_from_ctrl(thread));
U64 stack_top_unrounded = regs_rsp_from_arch_block(arch, regs_block);
U64 stack_base = AlignPow2(stack_base_unrounded, KB(4));
U64 stack_top = AlignDownPow2(stack_top_unrounded, KB(4));
U64 stack_size = stack_base - stack_top;
if(stack_base >= stack_top)
{
String8 stack_memory = {0};
stack_memory.str = push_array_no_zero(scratch.arena, U8, stack_size);
stack_memory.size = ctrl_process_read(machine_id, process, r1u64(stack_top, stack_top+stack_size), stack_memory.str);
if(stack_memory.size != 0)
{
stack_memview_good = 1;
stack_memview.data = stack_memory.str;
stack_memview.addr_first = stack_top;
stack_memview.addr_opl = stack_base;
}
}
}
// rjf: loop & unwind
UNW_MemView memview = stack_memview;
if(stack_memview_good) for(;;)
{
unwind.error = 0;
// rjf: regs -> rip*module*binary
U64 rip = regs_rip_from_arch_block(arch, regs_block);
CTRL_Handle module = ctrl_module_from_process_vaddr(machine_id, process, rip);
// rjf: cancel on 0 rip
if(rip == 0)
{
break;
}
// rjf: binary -> all the binary info
String8 binary_full_path = demon_full_path_from_module(scratch.arena, ctrl_demon_handle_from_ctrl(module));
DBGI_Parse *dbgi = dbgi_parse_from_exe_path(scope, binary_full_path, 0);
String8 binary_data = str8((U8 *)dbgi->exe_base, dbgi->exe_props.size);
// rjf: cancel on bad data
if(binary_data.size == 0)
{
unwind.error = 1;
break;
}
// rjf: valid step -> push frame
CTRL_UnwindFrame *frame = push_array(arena, CTRL_UnwindFrame, 1);
frame->rip = rip;
frame->regs = push_array_no_zero(arena, U8, arch_reg_block_size);
MemoryCopy(frame->regs, regs_block, arch_reg_block_size);
SLLQueuePush(unwind.first, unwind.last, frame);
unwind.count += 1;
// rjf: unwind one step
UNW_Result unwind_step = unw_pe_x64(binary_data, &dbgi->pe, demon_vaddr_range_from_module(ctrl_demon_handle_from_ctrl(module)).min, &memview, (UNW_X64_Regs *)regs_block);
// rjf: cancel on bad step
if(unwind_step.dead != 0)
{
break;
}
if(unwind_step.missed_read != 0)
{
unwind.error = 1;
break;
}
if(unwind_step.stack_pointer == 0)
{
break;
}
}
}break;
}
dbgi_scope_close(scope);
scratch_end(scratch);
ProfEnd();
return unwind;
}
//- rjf: name -> register/alias hash tables, for eval
internal EVAL_String2NumMap *
@@ -1419,6 +1559,67 @@ ctrl_thread__next_demon_event(Arena *arena, CTRL_Msg *msg, DEMON_RunCtrls *run_c
should_filter_event = 0;
}
}
// rjf: special case: be gracious with ASan modules or symbols if
// they do their cute little 0xc0000005 exception trick...
if(!should_filter_event && ev->code == 0xc0000005 &&
(spoof == 0 || ev->instruction_pointer != spoof->new_ip_value))
{
DBGI_Scope *scope = dbgi_scope_open();
DEMON_HandleArray modules = demon_modules_from_process(scratch.arena, ev->process);
if(modules.count != 0)
{
// rjf: determine base address of asan shadow space
U64 asan_shadow_base_vaddr = 0;
B32 asan_shadow_variable_exists_but_is_zero = 0;
CTRL_Handle module = ctrl_handle_from_demon(modules.handles[0]);
String8 module_path = demon_full_path_from_module(scratch.arena, ctrl_demon_handle_from_ctrl(module));
DBGI_Parse *dbgi = dbgi_parse_from_exe_path(scope, module_path, max_U64);
RADDBG_Parsed *rdbg = &dbgi->rdbg;
RADDBG_NameMap *unparsed_map = raddbg_name_map_from_kind(rdbg, RADDBG_NameMapKind_GlobalVariables);
if(rdbg->global_variables != 0 && unparsed_map != 0)
{
RADDBG_ParsedNameMap map = {0};
raddbg_name_map_parse(rdbg, unparsed_map, &map);
String8 name = str8_lit("__asan_shadow_memory_dynamic_address");
RADDBG_NameMapNode *node = raddbg_name_map_lookup(rdbg, &map, name.str, name.size);
if(node != 0)
{
U32 id_count = 0;
U32 *ids = raddbg_matches_from_map_node(rdbg, node, &id_count);
if(id_count > 0 && 0 < ids[0] && ids[0] < rdbg->global_variable_count)
{
RADDBG_GlobalVariable *global_var = &rdbg->global_variables[ids[0]];
U64 global_var_voff = global_var->voff;
U64 global_var_vaddr = global_var->voff + demon_base_vaddr_from_module(ctrl_demon_handle_from_ctrl(module));
Architecture arch = demon_arch_from_object(ev->thread);
U64 addr_size = bit_size_from_arch(arch)/8;
ctrl_process_read(CTRL_MachineID_Client, ctrl_handle_from_demon(ev->process), r1u64(global_var_vaddr, global_var_vaddr+addr_size), &asan_shadow_base_vaddr);
asan_shadow_variable_exists_but_is_zero = (asan_shadow_base_vaddr == 0);
}
}
}
// rjf: determine if this was a read/write to the shadow space
B32 violation_in_shadow_space = 0;
if(asan_shadow_base_vaddr != 0)
{
U64 asan_shadow_space_size = TB(128)/8;
if(asan_shadow_base_vaddr <= ev->address && ev->address < asan_shadow_base_vaddr+asan_shadow_space_size)
{
violation_in_shadow_space = 1;
}
}
// rjf: filter event if this violation occurred in asan's shadow space
if(violation_in_shadow_space || asan_shadow_variable_exists_but_is_zero)
{
should_filter_event = 1;
}
}
dbgi_scope_close(scope);
}
}break;
}
}
+26
View File
@@ -46,6 +46,26 @@ struct CTRL_MachineIDHandlePairList
U64 count;
};
////////////////////////////////
//~ rjf: Unwind Types
typedef struct CTRL_UnwindFrame CTRL_UnwindFrame;
struct CTRL_UnwindFrame
{
CTRL_UnwindFrame *next;
U64 rip;
void *regs;
};
typedef struct CTRL_Unwind CTRL_Unwind;
struct CTRL_Unwind
{
CTRL_UnwindFrame *first;
CTRL_UnwindFrame *last;
U64 count;
B32 error;
};
////////////////////////////////
//~ rjf: Trap Types
@@ -552,6 +572,12 @@ internal U64 ctrl_rip_from_thread(CTRL_MachineID machine_id, CTRL_Handle thread)
internal B32 ctrl_thread_write_rip(CTRL_MachineID machine_id, CTRL_Handle thread, U64 rip);
internal U64 ctrl_tls_root_vaddr_from_thread(CTRL_MachineID machine_id, CTRL_Handle thread);
//- rjf: process * vaddr -> module
internal CTRL_Handle ctrl_module_from_process_vaddr(CTRL_MachineID machine_id, CTRL_Handle process, U64 vaddr);
//- rjf: unwinding
internal CTRL_Unwind ctrl_unwind_from_thread(Arena *arena, CTRL_MachineID machine_id, CTRL_Handle thread);
//- rjf: name -> register/alias hash tables, for eval
internal EVAL_String2NumMap *ctrl_string2reg_from_arch(Architecture arch);
internal EVAL_String2NumMap *ctrl_string2alias_from_arch(Architecture arch);
+1
View File
@@ -861,6 +861,7 @@ demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){
// init new entity
DEMON_Entity *module = demon_ent_new(process, DEMON_EntityKind_Module, module_base);
module->addr_range_dim = image_info.size;
demon_module_count += 1;
DEMON_W32_Ext *module_ext = demon_w32_ext_alloc();
module->ext = module_ext;
+4 -4
View File
@@ -3501,14 +3501,14 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D
color.y *= 0.7f;
color.z *= 0.7f;
ui_set_next_background_color(color);
if(is_frozen && df_icon_buttonf(DF_IconKind_Locked, "Thaw").clicked)
if(is_frozen && df_icon_buttonf(DF_IconKind_Locked, "Thaw###freeze_thaw").clicked)
{
DF_CmdParams params = df_cmd_params_from_window(ws);
params.entity = df_handle_from_entity(entity);
df_cmd_params_mark_slot(&params, DF_CmdParamSlot_Entity);
df_push_cmd__root(&params, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_ThawEntity));
}
if(!is_frozen && df_icon_buttonf(DF_IconKind_Unlocked, "Freeze").clicked)
if(!is_frozen && df_icon_buttonf(DF_IconKind_Unlocked, "Freeze###freeze_thaw").clicked)
{
DF_CmdParams params = df_cmd_params_from_window(ws);
params.entity = df_handle_from_entity(entity);
@@ -3603,9 +3603,9 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D
B32 is_selected = df_handle_match(ctrl_ctx.thread, df_handle_from_entity(entity));
if(is_selected)
{
df_icon_buttonf(DF_IconKind_Thread, "[Selected]");
df_icon_buttonf(DF_IconKind_Thread, "[Selected]###select_entity");
}
else if(df_icon_buttonf(DF_IconKind_Thread, "Select").clicked)
else if(df_icon_buttonf(DF_IconKind_Thread, "Select###select_entity").clicked)
{
DF_CmdParams params = df_cmd_params_from_window(ws);
params.entity = df_handle_from_entity(entity);
-1
View File
@@ -1402,7 +1402,6 @@ extended_type_coverage_eval_tests(void){
non_virtual_derived->x += 1;
non_virtual_derived->x += 1;
non_virtual_derived->x += 1;
delete non_virtual_derived;
Base *base_array[1024] = {0};
for(int i = 0; i < sizeof(base_array)/sizeof(base_array[0]); i += 1)
-3
View File
@@ -17,8 +17,6 @@
// progress bar in the disassembly window. I had to close and restart. Is
// console app debugging not working yet, perhaps?
//
// [ ] register value committing in watch
//
// [ ] Setting the code_font/main_font values to a font name doesn't work.
// Should probably make note that you have to set it to a path to a TTF,
// since that's not normally how Windows fonts work.
@@ -27,7 +25,6 @@
//
// [ ] disasm animation & go-to-address
//
// [ ] visualize mismatched source code and debug info
// [ ] visualize remapped files (via path map)
////////////////////////////////