From ab0b26e876e1b77fe9272d0462b67786a2c761e3 Mon Sep 17 00:00:00 2001 From: Lucas Perlind Date: Thu, 24 Apr 2025 15:10:58 +1000 Subject: [PATCH 1/5] Add more asan support to the odin runtime and begin sanitizing allocators This adds various bindings to the asan runtime which can be used to poison/unpoison memory handed out by various allocators. This means we can catch use after free memory bugs when using operations such as free_all during runtime. Asan poisoning are added for the follow allocators in mem: Arena (including temporary arenas) Scratch Stack Small_Stack Additionally a bug in the stack allocator was fixed to disallow freeing in the middle of the stack (caught by the asan!). I plan on adding support for all the allocators in core. This is just a good starting point and were some of the easiest ones to implement asan for. --- base/runtime/asan.odin | 311 +++++++++++++++++++++++++++++++++++++++ core/mem/allocators.odin | 52 +++++-- 2 files changed, 353 insertions(+), 10 deletions(-) create mode 100644 base/runtime/asan.odin diff --git a/base/runtime/asan.odin b/base/runtime/asan.odin new file mode 100644 index 000000000..cdea4b2e0 --- /dev/null +++ b/base/runtime/asan.odin @@ -0,0 +1,311 @@ +#+no-instrumentation +package runtime + +Asan_Death_Callback :: #type proc "c" (pc: rawptr, bp: rawptr, sp: rawptr, addr: rawptr, is_write: i32, access_size: uint) + +@(private="file") +ASAN_ENABLED :: .Address in ODIN_SANITIZER_FLAGS + +@(private="file") +@(default_calling_convention="system") +foreign { + __asan_poison_memory_region :: proc(address: rawptr, size: uint) --- + __asan_unpoison_memory_region :: proc(address: rawptr, size: uint) --- + __sanitizer_set_death_callback :: proc(callback: Asan_Death_Callback) --- + __asan_region_is_poisoned :: proc(begin: rawptr, size: uint) -> rawptr --- + __asan_address_is_poisoned :: proc(addr: rawptr) -> i32 --- + __asan_describe_address :: proc(addr: rawptr) --- + __asan_report_present :: proc() -> i32 --- + __asan_get_report_pc :: proc() -> rawptr --- + __asan_get_report_bp :: proc() -> rawptr --- + __asan_get_report_sp :: proc() -> rawptr --- + __asan_get_report_address :: proc() -> rawptr --- + __asan_get_report_access_type :: proc() -> i32 --- + __asan_get_report_access_size :: proc() -> uint --- + __asan_get_report_description :: proc() -> cstring --- + __asan_locate_address :: proc(addr: rawptr, name: rawptr, name_size: uint, region_address: ^rawptr, region_size: ^uint) -> cstring --- + __asan_get_alloc_stack :: proc(addr: rawptr, trace: rawptr, size: uint, thread_id: ^i32) -> uint --- + __asan_get_free_stack :: proc(addr: rawptr, trace: rawptr, size: uint, thread_id: ^i32) -> uint --- + __asan_get_shadow_mapping :: proc(shadow_scale: ^uint, shadow_offset: ^uint) --- + __asan_print_accumulated_stats :: proc() --- + __asan_get_current_fake_stack :: proc() -> rawptr --- + __asan_addr_is_in_fake_stack :: proc(fake_stack: rawptr, addr: rawptr, beg: ^rawptr, end: ^rawptr) -> rawptr --- + __asan_handle_no_return :: proc() --- + __asan_update_allocation_context :: proc(addr: rawptr) -> i32 --- +} + +Asan_Access_Type :: enum { + none, + read, + write, +} + +Asan_Located_Address_String :: struct { + category: string, + name: string, +} + +Asan_Shadow_Mapping :: struct { + scale, offset: uint +} + +asan_poison_slice :: proc(region: $T/[]$E) { + when ASAN_ENABLED { + __asan_poison_memory_region(raw_data(region), size_of(E) * len(region)) + } +} + +asan_unpoison_slice :: proc(region: $T/[]$E) { + when ASAN_ENABLED { + __asan_unpoison_memory_region(raw_data(region), size_of(E) * len(region)) + } +} + +asan_poison_ptr :: proc(ptr: ^$T) { + when ASAN_ENABLED { + __asan_poison_memory_region(ptr, size_of(T)) + } +} + +asan_unpoison_ptr :: proc(ptr: ^$T) { + when ASAN_ENABLED { + __asan_unpoison_memory_region(ptr, size_of(T)) + } +} + +asan_poison_rawptr :: proc(ptr: rawptr, len: int) { + when ASAN_ENABLED { + assert(len >= 0) + __asan_poison_memory_region(ptr, uint(len)) + } +} + +asan_unpoison_rawptr :: proc(ptr: rawptr, len: int) { + when ASAN_ENABLED { + assert(len >= 0) + __asan_unpoison_memory_region(ptr, uint(len)) + } +} + +asan_poison :: proc { + asan_poison_slice, + asan_poison_ptr, + asan_poison_rawptr, +} + +asan_unpoison :: proc { + asan_unpoison_slice, + asan_unpoison_ptr, + asan_unpoison_rawptr, +} + +asan_set_death_callback :: proc(callback: Asan_Death_Callback) { + when ASAN_ENABLED { + __sanitizer_set_death_callback(callback) + } +} + +asan_region_is_poisoned_slice :: proc(region: []$T/$E) -> rawptr { + when ASAN_ENABLED { + return __asan_region_is_poisoned(raw_data(region), size_of(E) * len(region)) + } else { + return nil + } +} + +asan_region_is_poisoned_ptr :: proc(ptr: ^$T) -> rawptr { + when ASAN_ENABLED { + return __asan_region_is_poisoned(ptr, size_of(T)) + } else { + return nil + } +} + +asan_region_is_poisoned_rawptr :: proc(region: rawptr, len: int) -> rawptr { + when ASAN_ENABLED { + assert(len >= 0) + return __asan_region_is_poisoned(region, uint(len)) + } else { + return nil + } +} + +asan_region_is_poisoned :: proc { + asan_region_is_poisoned_slice, + asan_region_is_poisoned_ptr, + asan_region_is_poisoned_rawptr, +} + +asan_address_is_poisoned :: proc(address: rawptr) -> bool { + when ASAN_ENABLED { + return __asan_address_is_poisoned(address) != 0 + } else { + return false + } +} + +asan_describe_address :: proc(address: rawptr) { + when ASAN_ENABLED { + __asan_describe_address(address) + } +} + +asan_report_present :: proc() -> bool { + when ASAN_ENABLED { + return __asan_report_present() != 0 + } else { + return false + } +} + +asan_get_report_pc :: proc() -> rawptr { + when ASAN_ENABLED { + return __asan_get_report_pc() + } else { + return nil + } +} + +asan_get_report_bp :: proc() -> rawptr { + when ASAN_ENABLED { + return __asan_get_report_bp() + } else { + return nil + } +} + +asan_get_report_sp :: proc() -> rawptr { + when ASAN_ENABLED { + return __asan_get_report_sp() + } else { + return nil + } +} + +asan_get_report_address :: proc() -> rawptr { + when ASAN_ENABLED { + return __asan_get_report_address() + } else { + return nil + } +} + +asan_get_report_access_type :: proc() -> Asan_Access_Type { + when ASAN_ENABLED { + return __asan_get_report_access_type() == 0 ? .read : .write + } else { + return .none + } +} + +asan_get_report_access_size :: proc() -> uint { + when ASAN_ENABLED { + return __asan_get_report_access_size() + } else { + return 0 + } +} + +asan_get_report_description :: proc() -> string { + when ASAN_ENABLED { + return string(__asan_get_report_description()) + } else { + return "unknown" + } +} + +asan_locate_address :: proc(addr: rawptr, allocator: Allocator, string_alloc_size := 64) -> (Asan_Located_Address_String, []byte, Allocator_Error) { + when ASAN_ENABLED { + data, err := make([]byte, string_alloc_size, allocator) + if err != nil { + return { "", "" }, {}, err + } + out_addr: rawptr + out_size: uint + str := __asan_locate_address(addr, raw_data(data), len(data), &out_addr, &out_size) + return { string(str), string(cstring(raw_data(data))) }, (cast([^]byte)out_addr)[:out_size], nil + } else { + return { "", "" }, {}, nil + } +} + +asan_get_alloc_stack_trace :: proc(addr: rawptr, allocator: Allocator, stack_alloc_size := 32) -> ([]rawptr, int, Allocator_Error) { + when ASAN_ENABLED { + data, err := make([]rawptr, stack_alloc_size, allocator) + if err != nil { + return {}, 0, err + } + out_thread: i32 + __asan_get_alloc_stack(addr, raw_data(data), len(data), &out_thread) + return data, int(out_thread), nil + } else { + return {}, 0, nil + } +} + +asan_get_free_stack_trace :: proc(addr: rawptr, allocator: Allocator, stack_alloc_size := 32) -> ([]rawptr, int, Allocator_Error) { + when ASAN_ENABLED { + data, err := make([]rawptr, stack_alloc_size, allocator) + if err != nil { + return {}, 0, err + } + out_thread: i32 + __asan_get_free_stack(addr, raw_data(data), len(data), &out_thread) + return data, int(out_thread), nil + } else { + return {}, 0, nil + } +} + +asan_get_shadow_mapping :: proc() -> Asan_Shadow_Mapping { + when ASAN_ENABLED { + result: Asan_Shadow_Mapping + __asan_get_shadow_mapping(&result.scale, &result.offset) + return result + } else { + return {} + } +} + +asan_print_accumulated_stats :: proc() { + when ASAN_ENABLED { + __asan_print_accumulated_stats() + } +} + +asan_get_current_fake_stack :: proc() -> rawptr { + when ASAN_ENABLED { + return __asan_get_current_fake_stack() + } else { + return nil + } +} + +asan_is_in_fake_stack :: proc(fake_stack: rawptr, addr: rawptr) -> ([]byte, bool) { + when ASAN_ENABLED { + begin: rawptr + end: rawptr + addr := __asan_addr_is_in_fake_stack(fake_stack, addr, &begin, &end) + if addr == nil { + return {}, false + } + return ((cast([^]byte)begin)[:uintptr(end)-uintptr(begin)]), true + } else { + return {}, false + } +} + +asan_handle_no_return :: proc() { + when ASAN_ENABLED { + __asan_handle_no_return() + } +} + +asan_update_allocation_context :: proc(addr: rawptr) -> bool { + when ASAN_ENABLED { + return __asan_update_allocation_context(addr) != 0 + } else { + return false + } +} + diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 028be58e3..c5d7d70b9 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -138,6 +138,7 @@ arena_init :: proc(a: ^Arena, data: []byte) { a.offset = 0 a.peak_used = 0 a.temp_count = 0 + runtime.asan_poison(a.data) } /* @@ -224,7 +225,9 @@ arena_alloc_bytes_non_zeroed :: proc( } a.offset += total_size a.peak_used = max(a.peak_used, a.offset) - return byte_slice(ptr, size), nil + result := byte_slice(ptr, size) + runtime.asan_unpoison(result) + return result, nil } /* @@ -232,6 +235,7 @@ Free all memory to an arena. */ arena_free_all :: proc(a: ^Arena) { a.offset = 0 + runtime.asan_poison(a.data) } arena_allocator_proc :: proc( @@ -309,6 +313,7 @@ allocations *inside* the temporary memory region will be freed to the arena. end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) { assert(tmp.arena.offset >= tmp.prev_offset) assert(tmp.arena.temp_count > 0) + runtime.asan_poison(tmp.arena.data[tmp.prev_offset:tmp.arena.offset]) tmp.arena.offset = tmp.prev_offset tmp.arena.temp_count -= 1 } @@ -363,6 +368,7 @@ scratch_init :: proc(s: ^Scratch, size: int, backup_allocator := context.allocat s.prev_allocation = nil s.backup_allocator = backup_allocator s.leaked_allocations.allocator = backup_allocator + runtime.asan_poison(s.data) return nil } @@ -377,6 +383,7 @@ scratch_destroy :: proc(s: ^Scratch) { free_bytes(ptr, s.backup_allocator) } delete(s.leaked_allocations) + runtime.asan_unpoison(s.data) delete(s.data, s.backup_allocator) s^ = {} } @@ -472,7 +479,9 @@ scratch_alloc_bytes_non_zeroed :: proc( ptr := align_forward_uintptr(offset+start, uintptr(alignment)) s.prev_allocation = rawptr(ptr) s.curr_offset = int(offset) + size - return byte_slice(rawptr(ptr), size), nil + result := byte_slice(rawptr(ptr), size) + runtime.asan_unpoison(result) + return result, nil } else { a := s.backup_allocator if a.procedure == nil { @@ -516,6 +525,7 @@ scratch_free :: proc(s: ^Scratch, ptr: rawptr, loc := #caller_location) -> Alloc old_ptr := uintptr(ptr) if s.prev_allocation == ptr { s.curr_offset = int(uintptr(s.prev_allocation) - start) + runtime.asan_poison(s.data[s.curr_offset:]) s.prev_allocation = nil return nil } @@ -546,6 +556,7 @@ scratch_free_all :: proc(s: ^Scratch, loc := #caller_location) { free_bytes(ptr, s.backup_allocator, loc) } clear(&s.leaked_allocations) + runtime.asan_poison(s.data) } /* @@ -675,7 +686,9 @@ scratch_resize_bytes_non_zeroed :: proc( old_ptr := uintptr(old_memory) if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end { s.curr_offset = int(old_ptr-begin)+size - return byte_slice(old_memory, size), nil + result := byte_slice(old_memory, size) + runtime.asan_unpoison(result) + return result, nil } data, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc) if err != nil { @@ -776,6 +789,7 @@ stack_init :: proc(s: ^Stack, data: []byte) { s.prev_offset = 0 s.curr_offset = 0 s.peak_used = 0 + runtime.asan_poison(data) } /* @@ -861,15 +875,19 @@ stack_alloc_bytes_non_zeroed :: proc( if s.curr_offset + padding + size > len(s.data) { return nil, .Out_Of_Memory } + old_offset := s.prev_offset s.prev_offset = s.curr_offset s.curr_offset += padding next_addr := curr_addr + uintptr(padding) header := (^Stack_Allocation_Header)(next_addr - size_of(Stack_Allocation_Header)) + runtime.asan_unpoison(header) header.padding = padding - header.prev_offset = s.prev_offset + header.prev_offset = old_offset s.curr_offset += size s.peak_used = max(s.peak_used, s.curr_offset) - return byte_slice(rawptr(next_addr), size), nil + result := byte_slice(rawptr(next_addr), size) + runtime.asan_unpoison(result) + return result, nil } /* @@ -902,12 +920,15 @@ stack_free :: proc( } header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header)) old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data))) - if old_offset != header.prev_offset { + if old_offset != s.prev_offset { // panic("Out of order stack allocator free"); return .Invalid_Pointer } - s.curr_offset = old_offset + s.prev_offset = header.prev_offset + runtime.asan_poison(s.data[old_offset:s.curr_offset]) + s.curr_offset = old_offset + return nil } @@ -917,6 +938,7 @@ Free all allocations to the stack. stack_free_all :: proc(s: ^Stack, loc := #caller_location) { s.prev_offset = 0 s.curr_offset = 0 + runtime.asan_poison(s.data) } /* @@ -1076,7 +1098,9 @@ stack_resize_bytes_non_zeroed :: proc( if diff > 0 { zero(rawptr(curr_addr + uintptr(diff)), diff) } - return byte_slice(old_memory, size), nil + result := byte_slice(old_memory, size) + runtime.asan_unpoison(result) + return result, nil } stack_allocator_proc :: proc( @@ -1144,6 +1168,7 @@ small_stack_init :: proc(s: ^Small_Stack, data: []byte) { s.data = data s.offset = 0 s.peak_used = 0 + runtime.asan_poison(data) } /* @@ -1252,10 +1277,13 @@ small_stack_alloc_bytes_non_zeroed :: proc( s.offset += padding next_addr := curr_addr + uintptr(padding) header := (^Small_Stack_Allocation_Header)(next_addr - size_of(Small_Stack_Allocation_Header)) + runtime.asan_unpoison(header) header.padding = auto_cast padding s.offset += size s.peak_used = max(s.peak_used, s.offset) - return byte_slice(rawptr(next_addr), size), nil + result := byte_slice(rawptr(next_addr), size) + runtime.asan_unpoison(result) + return result, nil } /* @@ -1289,6 +1317,7 @@ small_stack_free :: proc( } header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header)) old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data))) + runtime.asan_poison(s.data[old_offset:s.offset]) s.offset = old_offset return nil } @@ -1298,6 +1327,7 @@ Free all memory to small stack. */ small_stack_free_all :: proc(s: ^Small_Stack) { s.offset = 0 + runtime.asan_poison(s.data) } /* @@ -1442,7 +1472,9 @@ small_stack_resize_bytes_non_zeroed :: proc( return nil, nil } if old_size == size { - return byte_slice(old_memory, size), nil + result := byte_slice(old_memory, size) + runtime.asan_unpoison(result) + return result, nil } data, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc) if err == nil { From 4a0be1f3a85dac5dfbe5e45adb4b4580b90a5834 Mon Sep 17 00:00:00 2001 From: Lucas Perlind Date: Thu, 24 Apr 2025 16:02:31 +1000 Subject: [PATCH 2/5] make vet happy --- base/runtime/asan.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/runtime/asan.odin b/base/runtime/asan.odin index cdea4b2e0..2a7ce0b58 100644 --- a/base/runtime/asan.odin +++ b/base/runtime/asan.odin @@ -46,7 +46,8 @@ Asan_Located_Address_String :: struct { } Asan_Shadow_Mapping :: struct { - scale, offset: uint + scale: uint, + offset: uint, } asan_poison_slice :: proc(region: $T/[]$E) { @@ -285,8 +286,7 @@ asan_is_in_fake_stack :: proc(fake_stack: rawptr, addr: rawptr) -> ([]byte, bool when ASAN_ENABLED { begin: rawptr end: rawptr - addr := __asan_addr_is_in_fake_stack(fake_stack, addr, &begin, &end) - if addr == nil { + if __asan_addr_is_in_fake_stack(fake_stack, addr, &begin, &end) == nil { return {}, false } return ((cast([^]byte)begin)[:uintptr(end)-uintptr(begin)]), true From 7502e7f2bc165fac89c62b85df698ded38a01196 Mon Sep 17 00:00:00 2001 From: Lucas Perlind Date: Thu, 24 Apr 2025 19:54:49 +1000 Subject: [PATCH 3/5] make asan procs contextless --- base/runtime/asan.odin | 64 +++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/base/runtime/asan.odin b/base/runtime/asan.odin index 2a7ce0b58..37072c3ea 100644 --- a/base/runtime/asan.odin +++ b/base/runtime/asan.odin @@ -50,40 +50,40 @@ Asan_Shadow_Mapping :: struct { offset: uint, } -asan_poison_slice :: proc(region: $T/[]$E) { +asan_poison_slice :: proc "contextless" (region: $T/[]$E) { when ASAN_ENABLED { __asan_poison_memory_region(raw_data(region), size_of(E) * len(region)) } } -asan_unpoison_slice :: proc(region: $T/[]$E) { +asan_unpoison_slice :: proc "contextless" (region: $T/[]$E) { when ASAN_ENABLED { __asan_unpoison_memory_region(raw_data(region), size_of(E) * len(region)) } } -asan_poison_ptr :: proc(ptr: ^$T) { +asan_poison_ptr :: proc "contextless" (ptr: ^$T) { when ASAN_ENABLED { __asan_poison_memory_region(ptr, size_of(T)) } } -asan_unpoison_ptr :: proc(ptr: ^$T) { +asan_unpoison_ptr :: proc "contextless" (ptr: ^$T) { when ASAN_ENABLED { __asan_unpoison_memory_region(ptr, size_of(T)) } } -asan_poison_rawptr :: proc(ptr: rawptr, len: int) { +asan_poison_rawptr :: proc "contextless" (ptr: rawptr, len: int) { when ASAN_ENABLED { - assert(len >= 0) + assert_contextless(len >= 0) __asan_poison_memory_region(ptr, uint(len)) } } -asan_unpoison_rawptr :: proc(ptr: rawptr, len: int) { +asan_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) { when ASAN_ENABLED { - assert(len >= 0) + assert_contextless(len >= 0) __asan_unpoison_memory_region(ptr, uint(len)) } } @@ -100,13 +100,13 @@ asan_unpoison :: proc { asan_unpoison_rawptr, } -asan_set_death_callback :: proc(callback: Asan_Death_Callback) { +asan_set_death_callback :: proc "contextless" (callback: Asan_Death_Callback) { when ASAN_ENABLED { __sanitizer_set_death_callback(callback) } } -asan_region_is_poisoned_slice :: proc(region: []$T/$E) -> rawptr { +asan_region_is_poisoned_slice :: proc "contextless" (region: []$T/$E) -> rawptr { when ASAN_ENABLED { return __asan_region_is_poisoned(raw_data(region), size_of(E) * len(region)) } else { @@ -114,7 +114,7 @@ asan_region_is_poisoned_slice :: proc(region: []$T/$E) -> rawptr { } } -asan_region_is_poisoned_ptr :: proc(ptr: ^$T) -> rawptr { +asan_region_is_poisoned_ptr :: proc "contextless" (ptr: ^$T) -> rawptr { when ASAN_ENABLED { return __asan_region_is_poisoned(ptr, size_of(T)) } else { @@ -122,9 +122,9 @@ asan_region_is_poisoned_ptr :: proc(ptr: ^$T) -> rawptr { } } -asan_region_is_poisoned_rawptr :: proc(region: rawptr, len: int) -> rawptr { +asan_region_is_poisoned_rawptr :: proc "contextless" (region: rawptr, len: int) -> rawptr { when ASAN_ENABLED { - assert(len >= 0) + assert_contextless(len >= 0) return __asan_region_is_poisoned(region, uint(len)) } else { return nil @@ -137,7 +137,7 @@ asan_region_is_poisoned :: proc { asan_region_is_poisoned_rawptr, } -asan_address_is_poisoned :: proc(address: rawptr) -> bool { +asan_address_is_poisoned :: proc "contextless" (address: rawptr) -> bool { when ASAN_ENABLED { return __asan_address_is_poisoned(address) != 0 } else { @@ -145,13 +145,13 @@ asan_address_is_poisoned :: proc(address: rawptr) -> bool { } } -asan_describe_address :: proc(address: rawptr) { +asan_describe_address :: proc "contextless" (address: rawptr) { when ASAN_ENABLED { __asan_describe_address(address) } } -asan_report_present :: proc() -> bool { +asan_report_present :: proc "contextless" () -> bool { when ASAN_ENABLED { return __asan_report_present() != 0 } else { @@ -159,7 +159,7 @@ asan_report_present :: proc() -> bool { } } -asan_get_report_pc :: proc() -> rawptr { +asan_get_report_pc :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_report_pc() } else { @@ -167,7 +167,7 @@ asan_get_report_pc :: proc() -> rawptr { } } -asan_get_report_bp :: proc() -> rawptr { +asan_get_report_bp :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_report_bp() } else { @@ -175,7 +175,7 @@ asan_get_report_bp :: proc() -> rawptr { } } -asan_get_report_sp :: proc() -> rawptr { +asan_get_report_sp :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_report_sp() } else { @@ -183,7 +183,7 @@ asan_get_report_sp :: proc() -> rawptr { } } -asan_get_report_address :: proc() -> rawptr { +asan_get_report_address :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_report_address() } else { @@ -191,7 +191,7 @@ asan_get_report_address :: proc() -> rawptr { } } -asan_get_report_access_type :: proc() -> Asan_Access_Type { +asan_get_report_access_type :: proc "contextless" () -> Asan_Access_Type { when ASAN_ENABLED { return __asan_get_report_access_type() == 0 ? .read : .write } else { @@ -199,7 +199,7 @@ asan_get_report_access_type :: proc() -> Asan_Access_Type { } } -asan_get_report_access_size :: proc() -> uint { +asan_get_report_access_size :: proc "contextless" () -> uint { when ASAN_ENABLED { return __asan_get_report_access_size() } else { @@ -207,7 +207,7 @@ asan_get_report_access_size :: proc() -> uint { } } -asan_get_report_description :: proc() -> string { +asan_get_report_description :: proc "contextless" () -> string { when ASAN_ENABLED { return string(__asan_get_report_description()) } else { @@ -215,7 +215,7 @@ asan_get_report_description :: proc() -> string { } } -asan_locate_address :: proc(addr: rawptr, allocator: Allocator, string_alloc_size := 64) -> (Asan_Located_Address_String, []byte, Allocator_Error) { +asan_locate_address :: proc (addr: rawptr, allocator: Allocator, string_alloc_size := 64) -> (Asan_Located_Address_String, []byte, Allocator_Error) { when ASAN_ENABLED { data, err := make([]byte, string_alloc_size, allocator) if err != nil { @@ -230,7 +230,7 @@ asan_locate_address :: proc(addr: rawptr, allocator: Allocator, string_alloc_siz } } -asan_get_alloc_stack_trace :: proc(addr: rawptr, allocator: Allocator, stack_alloc_size := 32) -> ([]rawptr, int, Allocator_Error) { +asan_get_alloc_stack_trace :: proc (addr: rawptr, allocator: Allocator, stack_alloc_size := 32) -> ([]rawptr, int, Allocator_Error) { when ASAN_ENABLED { data, err := make([]rawptr, stack_alloc_size, allocator) if err != nil { @@ -244,7 +244,7 @@ asan_get_alloc_stack_trace :: proc(addr: rawptr, allocator: Allocator, stack_all } } -asan_get_free_stack_trace :: proc(addr: rawptr, allocator: Allocator, stack_alloc_size := 32) -> ([]rawptr, int, Allocator_Error) { +asan_get_free_stack_trace :: proc (addr: rawptr, allocator: Allocator, stack_alloc_size := 32) -> ([]rawptr, int, Allocator_Error) { when ASAN_ENABLED { data, err := make([]rawptr, stack_alloc_size, allocator) if err != nil { @@ -258,7 +258,7 @@ asan_get_free_stack_trace :: proc(addr: rawptr, allocator: Allocator, stack_allo } } -asan_get_shadow_mapping :: proc() -> Asan_Shadow_Mapping { +asan_get_shadow_mapping :: proc "contextless" () -> Asan_Shadow_Mapping { when ASAN_ENABLED { result: Asan_Shadow_Mapping __asan_get_shadow_mapping(&result.scale, &result.offset) @@ -268,13 +268,13 @@ asan_get_shadow_mapping :: proc() -> Asan_Shadow_Mapping { } } -asan_print_accumulated_stats :: proc() { +asan_print_accumulated_stats :: proc "contextless" () { when ASAN_ENABLED { __asan_print_accumulated_stats() } } -asan_get_current_fake_stack :: proc() -> rawptr { +asan_get_current_fake_stack :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_current_fake_stack() } else { @@ -282,7 +282,7 @@ asan_get_current_fake_stack :: proc() -> rawptr { } } -asan_is_in_fake_stack :: proc(fake_stack: rawptr, addr: rawptr) -> ([]byte, bool) { +asan_is_in_fake_stack :: proc "contextless" (fake_stack: rawptr, addr: rawptr) -> ([]byte, bool) { when ASAN_ENABLED { begin: rawptr end: rawptr @@ -295,13 +295,13 @@ asan_is_in_fake_stack :: proc(fake_stack: rawptr, addr: rawptr) -> ([]byte, bool } } -asan_handle_no_return :: proc() { +asan_handle_no_return :: proc "contextless" () { when ASAN_ENABLED { __asan_handle_no_return() } } -asan_update_allocation_context :: proc(addr: rawptr) -> bool { +asan_update_allocation_context :: proc "contextless" (addr: rawptr) -> bool { when ASAN_ENABLED { return __asan_update_allocation_context(addr) != 0 } else { From 5c117bde6de7affac2a07d531e8bd75b007043fb Mon Sep 17 00:00:00 2001 From: Lucas Perlind Date: Thu, 24 Apr 2025 20:28:32 +1000 Subject: [PATCH 4/5] Add base:sanitizer package --- .../asan.odin => sanitizer/address.odin} | 98 ++++++++++--------- core/mem/allocators.odin | 45 ++++----- 2 files changed, 73 insertions(+), 70 deletions(-) rename base/{runtime/asan.odin => sanitizer/address.odin} (64%) diff --git a/base/runtime/asan.odin b/base/sanitizer/address.odin similarity index 64% rename from base/runtime/asan.odin rename to base/sanitizer/address.odin index 37072c3ea..e95348bfd 100644 --- a/base/runtime/asan.odin +++ b/base/sanitizer/address.odin @@ -1,7 +1,9 @@ #+no-instrumentation -package runtime +package sanitizer -Asan_Death_Callback :: #type proc "c" (pc: rawptr, bp: rawptr, sp: rawptr, addr: rawptr, is_write: i32, access_size: uint) +import "base:runtime" + +Address_Death_Callback :: #type proc "c" (pc: rawptr, bp: rawptr, sp: rawptr, addr: rawptr, is_write: i32, access_size: uint) @(private="file") ASAN_ENABLED :: .Address in ODIN_SANITIZER_FLAGS @@ -11,7 +13,7 @@ ASAN_ENABLED :: .Address in ODIN_SANITIZER_FLAGS foreign { __asan_poison_memory_region :: proc(address: rawptr, size: uint) --- __asan_unpoison_memory_region :: proc(address: rawptr, size: uint) --- - __sanitizer_set_death_callback :: proc(callback: Asan_Death_Callback) --- + __sanitizer_set_death_callback :: proc(callback: Address_Death_Callback) --- __asan_region_is_poisoned :: proc(begin: rawptr, size: uint) -> rawptr --- __asan_address_is_poisoned :: proc(addr: rawptr) -> i32 --- __asan_describe_address :: proc(addr: rawptr) --- @@ -34,79 +36,79 @@ foreign { __asan_update_allocation_context :: proc(addr: rawptr) -> i32 --- } -Asan_Access_Type :: enum { +Address_Access_Type :: enum { none, read, write, } -Asan_Located_Address_String :: struct { +Address_Located_Address_String :: struct { category: string, name: string, } -Asan_Shadow_Mapping :: struct { +Address_Shadow_Mapping :: struct { scale: uint, offset: uint, } -asan_poison_slice :: proc "contextless" (region: $T/[]$E) { +address_poison_slice :: proc "contextless" (region: $T/[]$E) { when ASAN_ENABLED { __asan_poison_memory_region(raw_data(region), size_of(E) * len(region)) } } -asan_unpoison_slice :: proc "contextless" (region: $T/[]$E) { +address_unpoison_slice :: proc "contextless" (region: $T/[]$E) { when ASAN_ENABLED { __asan_unpoison_memory_region(raw_data(region), size_of(E) * len(region)) } } -asan_poison_ptr :: proc "contextless" (ptr: ^$T) { +address_poison_ptr :: proc "contextless" (ptr: ^$T) { when ASAN_ENABLED { __asan_poison_memory_region(ptr, size_of(T)) } } -asan_unpoison_ptr :: proc "contextless" (ptr: ^$T) { +address_unpoison_ptr :: proc "contextless" (ptr: ^$T) { when ASAN_ENABLED { __asan_unpoison_memory_region(ptr, size_of(T)) } } -asan_poison_rawptr :: proc "contextless" (ptr: rawptr, len: int) { +address_poison_rawptr :: proc "contextless" (ptr: rawptr, len: int) { when ASAN_ENABLED { assert_contextless(len >= 0) __asan_poison_memory_region(ptr, uint(len)) } } -asan_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) { +address_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) { when ASAN_ENABLED { assert_contextless(len >= 0) __asan_unpoison_memory_region(ptr, uint(len)) } } -asan_poison :: proc { - asan_poison_slice, - asan_poison_ptr, - asan_poison_rawptr, +address_poison :: proc { + address_poison_slice, + address_poison_ptr, + address_poison_rawptr, } -asan_unpoison :: proc { - asan_unpoison_slice, - asan_unpoison_ptr, - asan_unpoison_rawptr, +address_unpoison :: proc { + address_unpoison_slice, + address_unpoison_ptr, + address_unpoison_rawptr, } -asan_set_death_callback :: proc "contextless" (callback: Asan_Death_Callback) { +address_set_death_callback :: proc "contextless" (callback: Address_Death_Callback) { when ASAN_ENABLED { __sanitizer_set_death_callback(callback) } } -asan_region_is_poisoned_slice :: proc "contextless" (region: []$T/$E) -> rawptr { +address_region_is_poisoned_slice :: proc "contextless" (region: []$T/$E) -> rawptr { when ASAN_ENABLED { return __asan_region_is_poisoned(raw_data(region), size_of(E) * len(region)) } else { @@ -114,7 +116,7 @@ asan_region_is_poisoned_slice :: proc "contextless" (region: []$T/$E) -> rawptr } } -asan_region_is_poisoned_ptr :: proc "contextless" (ptr: ^$T) -> rawptr { +address_region_is_poisoned_ptr :: proc "contextless" (ptr: ^$T) -> rawptr { when ASAN_ENABLED { return __asan_region_is_poisoned(ptr, size_of(T)) } else { @@ -122,7 +124,7 @@ asan_region_is_poisoned_ptr :: proc "contextless" (ptr: ^$T) -> rawptr { } } -asan_region_is_poisoned_rawptr :: proc "contextless" (region: rawptr, len: int) -> rawptr { +address_region_is_poisoned_rawptr :: proc "contextless" (region: rawptr, len: int) -> rawptr { when ASAN_ENABLED { assert_contextless(len >= 0) return __asan_region_is_poisoned(region, uint(len)) @@ -131,13 +133,13 @@ asan_region_is_poisoned_rawptr :: proc "contextless" (region: rawptr, len: int) } } -asan_region_is_poisoned :: proc { - asan_region_is_poisoned_slice, - asan_region_is_poisoned_ptr, - asan_region_is_poisoned_rawptr, +address_region_is_poisoned :: proc { + address_region_is_poisoned_slice, + address_region_is_poisoned_ptr, + address_region_is_poisoned_rawptr, } -asan_address_is_poisoned :: proc "contextless" (address: rawptr) -> bool { +address_address_is_poisoned :: proc "contextless" (address: rawptr) -> bool { when ASAN_ENABLED { return __asan_address_is_poisoned(address) != 0 } else { @@ -145,13 +147,13 @@ asan_address_is_poisoned :: proc "contextless" (address: rawptr) -> bool { } } -asan_describe_address :: proc "contextless" (address: rawptr) { +address_describe_address :: proc "contextless" (address: rawptr) { when ASAN_ENABLED { __asan_describe_address(address) } } -asan_report_present :: proc "contextless" () -> bool { +address_report_present :: proc "contextless" () -> bool { when ASAN_ENABLED { return __asan_report_present() != 0 } else { @@ -159,7 +161,7 @@ asan_report_present :: proc "contextless" () -> bool { } } -asan_get_report_pc :: proc "contextless" () -> rawptr { +address_get_report_pc :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_report_pc() } else { @@ -167,7 +169,7 @@ asan_get_report_pc :: proc "contextless" () -> rawptr { } } -asan_get_report_bp :: proc "contextless" () -> rawptr { +address_get_report_bp :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_report_bp() } else { @@ -175,7 +177,7 @@ asan_get_report_bp :: proc "contextless" () -> rawptr { } } -asan_get_report_sp :: proc "contextless" () -> rawptr { +address_get_report_sp :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_report_sp() } else { @@ -183,7 +185,7 @@ asan_get_report_sp :: proc "contextless" () -> rawptr { } } -asan_get_report_address :: proc "contextless" () -> rawptr { +address_get_report_address :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_report_address() } else { @@ -191,7 +193,7 @@ asan_get_report_address :: proc "contextless" () -> rawptr { } } -asan_get_report_access_type :: proc "contextless" () -> Asan_Access_Type { +address_get_report_access_type :: proc "contextless" () -> Address_Access_Type { when ASAN_ENABLED { return __asan_get_report_access_type() == 0 ? .read : .write } else { @@ -199,7 +201,7 @@ asan_get_report_access_type :: proc "contextless" () -> Asan_Access_Type { } } -asan_get_report_access_size :: proc "contextless" () -> uint { +address_get_report_access_size :: proc "contextless" () -> uint { when ASAN_ENABLED { return __asan_get_report_access_size() } else { @@ -207,7 +209,7 @@ asan_get_report_access_size :: proc "contextless" () -> uint { } } -asan_get_report_description :: proc "contextless" () -> string { +address_get_report_description :: proc "contextless" () -> string { when ASAN_ENABLED { return string(__asan_get_report_description()) } else { @@ -215,7 +217,7 @@ asan_get_report_description :: proc "contextless" () -> string { } } -asan_locate_address :: proc (addr: rawptr, allocator: Allocator, string_alloc_size := 64) -> (Asan_Located_Address_String, []byte, Allocator_Error) { +address_locate_address :: proc (addr: rawptr, allocator: runtime.Allocator, string_alloc_size := 64) -> (Address_Located_Address_String, []byte, runtime.Allocator_Error) { when ASAN_ENABLED { data, err := make([]byte, string_alloc_size, allocator) if err != nil { @@ -230,7 +232,7 @@ asan_locate_address :: proc (addr: rawptr, allocator: Allocator, string_alloc_si } } -asan_get_alloc_stack_trace :: proc (addr: rawptr, allocator: Allocator, stack_alloc_size := 32) -> ([]rawptr, int, Allocator_Error) { +address_get_alloc_stack_trace :: proc (addr: rawptr, allocator: runtime.Allocator, stack_alloc_size := 32) -> ([]rawptr, int, runtime.Allocator_Error) { when ASAN_ENABLED { data, err := make([]rawptr, stack_alloc_size, allocator) if err != nil { @@ -244,7 +246,7 @@ asan_get_alloc_stack_trace :: proc (addr: rawptr, allocator: Allocator, stack_al } } -asan_get_free_stack_trace :: proc (addr: rawptr, allocator: Allocator, stack_alloc_size := 32) -> ([]rawptr, int, Allocator_Error) { +address_get_free_stack_trace :: proc (addr: rawptr, allocator: runtime.Allocator, stack_alloc_size := 32) -> ([]rawptr, int, runtime.Allocator_Error) { when ASAN_ENABLED { data, err := make([]rawptr, stack_alloc_size, allocator) if err != nil { @@ -258,9 +260,9 @@ asan_get_free_stack_trace :: proc (addr: rawptr, allocator: Allocator, stack_all } } -asan_get_shadow_mapping :: proc "contextless" () -> Asan_Shadow_Mapping { +address_get_shadow_mapping :: proc "contextless" () -> Address_Shadow_Mapping { when ASAN_ENABLED { - result: Asan_Shadow_Mapping + result: Address_Shadow_Mapping __asan_get_shadow_mapping(&result.scale, &result.offset) return result } else { @@ -268,13 +270,13 @@ asan_get_shadow_mapping :: proc "contextless" () -> Asan_Shadow_Mapping { } } -asan_print_accumulated_stats :: proc "contextless" () { +address_print_accumulated_stats :: proc "contextless" () { when ASAN_ENABLED { __asan_print_accumulated_stats() } } -asan_get_current_fake_stack :: proc "contextless" () -> rawptr { +address_get_current_fake_stack :: proc "contextless" () -> rawptr { when ASAN_ENABLED { return __asan_get_current_fake_stack() } else { @@ -282,7 +284,7 @@ asan_get_current_fake_stack :: proc "contextless" () -> rawptr { } } -asan_is_in_fake_stack :: proc "contextless" (fake_stack: rawptr, addr: rawptr) -> ([]byte, bool) { +address_is_in_fake_stack :: proc "contextless" (fake_stack: rawptr, addr: rawptr) -> ([]byte, bool) { when ASAN_ENABLED { begin: rawptr end: rawptr @@ -295,13 +297,13 @@ asan_is_in_fake_stack :: proc "contextless" (fake_stack: rawptr, addr: rawptr) - } } -asan_handle_no_return :: proc "contextless" () { +address_handle_no_return :: proc "contextless" () { when ASAN_ENABLED { __asan_handle_no_return() } } -asan_update_allocation_context :: proc "contextless" (addr: rawptr) -> bool { +address_update_allocation_context :: proc "contextless" (addr: rawptr) -> bool { when ASAN_ENABLED { return __asan_update_allocation_context(addr) != 0 } else { diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index c5d7d70b9..1bb887212 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -2,6 +2,7 @@ package mem import "base:intrinsics" import "base:runtime" +import "base:sanitizer" /* Nil allocator. @@ -138,7 +139,7 @@ arena_init :: proc(a: ^Arena, data: []byte) { a.offset = 0 a.peak_used = 0 a.temp_count = 0 - runtime.asan_poison(a.data) + sanitizer.address_poison(a.data) } /* @@ -226,7 +227,7 @@ arena_alloc_bytes_non_zeroed :: proc( a.offset += total_size a.peak_used = max(a.peak_used, a.offset) result := byte_slice(ptr, size) - runtime.asan_unpoison(result) + sanitizer.address_unpoison(result) return result, nil } @@ -235,7 +236,7 @@ Free all memory to an arena. */ arena_free_all :: proc(a: ^Arena) { a.offset = 0 - runtime.asan_poison(a.data) + sanitizer.address_poison(a.data) } arena_allocator_proc :: proc( @@ -313,7 +314,7 @@ allocations *inside* the temporary memory region will be freed to the arena. end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) { assert(tmp.arena.offset >= tmp.prev_offset) assert(tmp.arena.temp_count > 0) - runtime.asan_poison(tmp.arena.data[tmp.prev_offset:tmp.arena.offset]) + sanitizer.address_poison(tmp.arena.data[tmp.prev_offset:tmp.arena.offset]) tmp.arena.offset = tmp.prev_offset tmp.arena.temp_count -= 1 } @@ -368,7 +369,7 @@ scratch_init :: proc(s: ^Scratch, size: int, backup_allocator := context.allocat s.prev_allocation = nil s.backup_allocator = backup_allocator s.leaked_allocations.allocator = backup_allocator - runtime.asan_poison(s.data) + sanitizer.address_poison(s.data) return nil } @@ -383,7 +384,7 @@ scratch_destroy :: proc(s: ^Scratch) { free_bytes(ptr, s.backup_allocator) } delete(s.leaked_allocations) - runtime.asan_unpoison(s.data) + sanitizer.address_unpoison(s.data) delete(s.data, s.backup_allocator) s^ = {} } @@ -480,7 +481,7 @@ scratch_alloc_bytes_non_zeroed :: proc( s.prev_allocation = rawptr(ptr) s.curr_offset = int(offset) + size result := byte_slice(rawptr(ptr), size) - runtime.asan_unpoison(result) + sanitizer.address_unpoison(result) return result, nil } else { a := s.backup_allocator @@ -525,7 +526,7 @@ scratch_free :: proc(s: ^Scratch, ptr: rawptr, loc := #caller_location) -> Alloc old_ptr := uintptr(ptr) if s.prev_allocation == ptr { s.curr_offset = int(uintptr(s.prev_allocation) - start) - runtime.asan_poison(s.data[s.curr_offset:]) + sanitizer.address_poison(s.data[s.curr_offset:]) s.prev_allocation = nil return nil } @@ -556,7 +557,7 @@ scratch_free_all :: proc(s: ^Scratch, loc := #caller_location) { free_bytes(ptr, s.backup_allocator, loc) } clear(&s.leaked_allocations) - runtime.asan_poison(s.data) + sanitizer.address_poison(s.data) } /* @@ -687,7 +688,7 @@ scratch_resize_bytes_non_zeroed :: proc( if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end { s.curr_offset = int(old_ptr-begin)+size result := byte_slice(old_memory, size) - runtime.asan_unpoison(result) + sanitizer.address_unpoison(result) return result, nil } data, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc) @@ -789,7 +790,7 @@ stack_init :: proc(s: ^Stack, data: []byte) { s.prev_offset = 0 s.curr_offset = 0 s.peak_used = 0 - runtime.asan_poison(data) + sanitizer.address_poison(data) } /* @@ -880,13 +881,13 @@ stack_alloc_bytes_non_zeroed :: proc( s.curr_offset += padding next_addr := curr_addr + uintptr(padding) header := (^Stack_Allocation_Header)(next_addr - size_of(Stack_Allocation_Header)) - runtime.asan_unpoison(header) + sanitizer.address_unpoison(header) header.padding = padding header.prev_offset = old_offset s.curr_offset += size s.peak_used = max(s.peak_used, s.curr_offset) result := byte_slice(rawptr(next_addr), size) - runtime.asan_unpoison(result) + sanitizer.address_unpoison(result) return result, nil } @@ -926,7 +927,7 @@ stack_free :: proc( } s.prev_offset = header.prev_offset - runtime.asan_poison(s.data[old_offset:s.curr_offset]) + sanitizer.address_poison(s.data[old_offset:s.curr_offset]) s.curr_offset = old_offset return nil @@ -938,7 +939,7 @@ Free all allocations to the stack. stack_free_all :: proc(s: ^Stack, loc := #caller_location) { s.prev_offset = 0 s.curr_offset = 0 - runtime.asan_poison(s.data) + sanitizer.address_poison(s.data) } /* @@ -1099,7 +1100,7 @@ stack_resize_bytes_non_zeroed :: proc( zero(rawptr(curr_addr + uintptr(diff)), diff) } result := byte_slice(old_memory, size) - runtime.asan_unpoison(result) + sanitizer.address_unpoison(result) return result, nil } @@ -1168,7 +1169,7 @@ small_stack_init :: proc(s: ^Small_Stack, data: []byte) { s.data = data s.offset = 0 s.peak_used = 0 - runtime.asan_poison(data) + sanitizer.address_poison(data) } /* @@ -1277,12 +1278,12 @@ small_stack_alloc_bytes_non_zeroed :: proc( s.offset += padding next_addr := curr_addr + uintptr(padding) header := (^Small_Stack_Allocation_Header)(next_addr - size_of(Small_Stack_Allocation_Header)) - runtime.asan_unpoison(header) + sanitizer.address_unpoison(header) header.padding = auto_cast padding s.offset += size s.peak_used = max(s.peak_used, s.offset) result := byte_slice(rawptr(next_addr), size) - runtime.asan_unpoison(result) + sanitizer.address_unpoison(result) return result, nil } @@ -1317,7 +1318,7 @@ small_stack_free :: proc( } header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header)) old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data))) - runtime.asan_poison(s.data[old_offset:s.offset]) + sanitizer.address_poison(s.data[old_offset:s.offset]) s.offset = old_offset return nil } @@ -1327,7 +1328,7 @@ Free all memory to small stack. */ small_stack_free_all :: proc(s: ^Small_Stack) { s.offset = 0 - runtime.asan_poison(s.data) + sanitizer.address_poison(s.data) } /* @@ -1473,7 +1474,7 @@ small_stack_resize_bytes_non_zeroed :: proc( } if old_size == size { result := byte_slice(old_memory, size) - runtime.asan_unpoison(result) + sanitizer.address_unpoison(result) return result, nil } data, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc) From 5e985bcd748cabd3b09f00372ffbcfdb8b7cd550 Mon Sep 17 00:00:00 2001 From: Lucas Perlind Date: Thu, 24 Apr 2025 20:33:08 +1000 Subject: [PATCH 5/5] Remove dependency on runtime; Add to examples --- base/sanitizer/address.odin | 32 +++++++++----------------------- examples/all/all_main.odin | 2 ++ 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/base/sanitizer/address.odin b/base/sanitizer/address.odin index e95348bfd..43c04a15d 100644 --- a/base/sanitizer/address.odin +++ b/base/sanitizer/address.odin @@ -1,8 +1,6 @@ #+no-instrumentation package sanitizer -import "base:runtime" - Address_Death_Callback :: #type proc "c" (pc: rawptr, bp: rawptr, sp: rawptr, addr: rawptr, is_write: i32, access_size: uint) @(private="file") @@ -217,46 +215,34 @@ address_get_report_description :: proc "contextless" () -> string { } } -address_locate_address :: proc (addr: rawptr, allocator: runtime.Allocator, string_alloc_size := 64) -> (Address_Located_Address_String, []byte, runtime.Allocator_Error) { +address_locate_address :: proc "contextless" (addr: rawptr, data: []byte) -> (Address_Located_Address_String, []byte) { when ASAN_ENABLED { - data, err := make([]byte, string_alloc_size, allocator) - if err != nil { - return { "", "" }, {}, err - } out_addr: rawptr out_size: uint str := __asan_locate_address(addr, raw_data(data), len(data), &out_addr, &out_size) - return { string(str), string(cstring(raw_data(data))) }, (cast([^]byte)out_addr)[:out_size], nil + return { string(str), string(cstring(raw_data(data))) }, (cast([^]byte)out_addr)[:out_size] } else { - return { "", "" }, {}, nil + return { "", "" }, {} } } -address_get_alloc_stack_trace :: proc (addr: rawptr, allocator: runtime.Allocator, stack_alloc_size := 32) -> ([]rawptr, int, runtime.Allocator_Error) { +address_get_alloc_stack_trace :: proc "contextless" (addr: rawptr, data: []rawptr) -> ([]rawptr, int) { when ASAN_ENABLED { - data, err := make([]rawptr, stack_alloc_size, allocator) - if err != nil { - return {}, 0, err - } out_thread: i32 __asan_get_alloc_stack(addr, raw_data(data), len(data), &out_thread) - return data, int(out_thread), nil + return data, int(out_thread) } else { - return {}, 0, nil + return {}, 0 } } -address_get_free_stack_trace :: proc (addr: rawptr, allocator: runtime.Allocator, stack_alloc_size := 32) -> ([]rawptr, int, runtime.Allocator_Error) { +address_get_free_stack_trace :: proc "contextless" (addr: rawptr, data: []rawptr) -> ([]rawptr, int) { when ASAN_ENABLED { - data, err := make([]rawptr, stack_alloc_size, allocator) - if err != nil { - return {}, 0, err - } out_thread: i32 __asan_get_free_stack(addr, raw_data(data), len(data), &out_thread) - return data, int(out_thread), nil + return data, int(out_thread) } else { - return {}, 0, nil + return {}, 0 } } diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 0e7648f96..0a17227b8 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -118,6 +118,7 @@ import relative "core:relative" import reflect "core:reflect" import runtime "base:runtime" +import sanitizer "base:sanitizer" import simd "core:simd" import x86 "core:simd/x86" import slice "core:slice" @@ -275,3 +276,4 @@ _ :: uuid_legacy _ :: utf8 _ :: utf8string _ :: utf16 +_ :: sanitizer