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;
}
}