mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-20 12:44:59 -07:00
Merge pull request #5072 from Lperlind/asan
Add more asan support to the odin runtime and begin sanitizing allocators
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
#+no-instrumentation
|
||||
package sanitizer
|
||||
|
||||
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
|
||||
|
||||
@(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: 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) ---
|
||||
__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 ---
|
||||
}
|
||||
|
||||
Address_Access_Type :: enum {
|
||||
none,
|
||||
read,
|
||||
write,
|
||||
}
|
||||
|
||||
Address_Located_Address_String :: struct {
|
||||
category: string,
|
||||
name: string,
|
||||
}
|
||||
|
||||
Address_Shadow_Mapping :: struct {
|
||||
scale: uint,
|
||||
offset: uint,
|
||||
}
|
||||
|
||||
address_poison_slice :: proc "contextless" (region: $T/[]$E) {
|
||||
when ASAN_ENABLED {
|
||||
__asan_poison_memory_region(raw_data(region), size_of(E) * len(region))
|
||||
}
|
||||
}
|
||||
|
||||
address_unpoison_slice :: proc "contextless" (region: $T/[]$E) {
|
||||
when ASAN_ENABLED {
|
||||
__asan_unpoison_memory_region(raw_data(region), size_of(E) * len(region))
|
||||
}
|
||||
}
|
||||
|
||||
address_poison_ptr :: proc "contextless" (ptr: ^$T) {
|
||||
when ASAN_ENABLED {
|
||||
__asan_poison_memory_region(ptr, size_of(T))
|
||||
}
|
||||
}
|
||||
|
||||
address_unpoison_ptr :: proc "contextless" (ptr: ^$T) {
|
||||
when ASAN_ENABLED {
|
||||
__asan_unpoison_memory_region(ptr, size_of(T))
|
||||
}
|
||||
}
|
||||
|
||||
address_poison_rawptr :: proc "contextless" (ptr: rawptr, len: int) {
|
||||
when ASAN_ENABLED {
|
||||
assert_contextless(len >= 0)
|
||||
__asan_poison_memory_region(ptr, uint(len))
|
||||
}
|
||||
}
|
||||
|
||||
address_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) {
|
||||
when ASAN_ENABLED {
|
||||
assert_contextless(len >= 0)
|
||||
__asan_unpoison_memory_region(ptr, uint(len))
|
||||
}
|
||||
}
|
||||
|
||||
address_poison :: proc {
|
||||
address_poison_slice,
|
||||
address_poison_ptr,
|
||||
address_poison_rawptr,
|
||||
}
|
||||
|
||||
address_unpoison :: proc {
|
||||
address_unpoison_slice,
|
||||
address_unpoison_ptr,
|
||||
address_unpoison_rawptr,
|
||||
}
|
||||
|
||||
address_set_death_callback :: proc "contextless" (callback: Address_Death_Callback) {
|
||||
when ASAN_ENABLED {
|
||||
__sanitizer_set_death_callback(callback)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
address_region_is_poisoned_ptr :: proc "contextless" (ptr: ^$T) -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_region_is_poisoned(ptr, size_of(T))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
address_region_is_poisoned :: proc {
|
||||
address_region_is_poisoned_slice,
|
||||
address_region_is_poisoned_ptr,
|
||||
address_region_is_poisoned_rawptr,
|
||||
}
|
||||
|
||||
address_address_is_poisoned :: proc "contextless" (address: rawptr) -> bool {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_address_is_poisoned(address) != 0
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
address_describe_address :: proc "contextless" (address: rawptr) {
|
||||
when ASAN_ENABLED {
|
||||
__asan_describe_address(address)
|
||||
}
|
||||
}
|
||||
|
||||
address_report_present :: proc "contextless" () -> bool {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_report_present() != 0
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
address_get_report_pc :: proc "contextless" () -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_report_pc()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
address_get_report_bp :: proc "contextless" () -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_report_bp()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
address_get_report_sp :: proc "contextless" () -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_report_sp()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
address_get_report_address :: proc "contextless" () -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_report_address()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
address_get_report_access_type :: proc "contextless" () -> Address_Access_Type {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_report_access_type() == 0 ? .read : .write
|
||||
} else {
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
address_get_report_access_size :: proc "contextless" () -> uint {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_report_access_size()
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
address_get_report_description :: proc "contextless" () -> string {
|
||||
when ASAN_ENABLED {
|
||||
return string(__asan_get_report_description())
|
||||
} else {
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
address_locate_address :: proc "contextless" (addr: rawptr, data: []byte) -> (Address_Located_Address_String, []byte) {
|
||||
when ASAN_ENABLED {
|
||||
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]
|
||||
} else {
|
||||
return { "", "" }, {}
|
||||
}
|
||||
}
|
||||
|
||||
address_get_alloc_stack_trace :: proc "contextless" (addr: rawptr, data: []rawptr) -> ([]rawptr, int) {
|
||||
when ASAN_ENABLED {
|
||||
out_thread: i32
|
||||
__asan_get_alloc_stack(addr, raw_data(data), len(data), &out_thread)
|
||||
return data, int(out_thread)
|
||||
} else {
|
||||
return {}, 0
|
||||
}
|
||||
}
|
||||
|
||||
address_get_free_stack_trace :: proc "contextless" (addr: rawptr, data: []rawptr) -> ([]rawptr, int) {
|
||||
when ASAN_ENABLED {
|
||||
out_thread: i32
|
||||
__asan_get_free_stack(addr, raw_data(data), len(data), &out_thread)
|
||||
return data, int(out_thread)
|
||||
} else {
|
||||
return {}, 0
|
||||
}
|
||||
}
|
||||
|
||||
address_get_shadow_mapping :: proc "contextless" () -> Address_Shadow_Mapping {
|
||||
when ASAN_ENABLED {
|
||||
result: Address_Shadow_Mapping
|
||||
__asan_get_shadow_mapping(&result.scale, &result.offset)
|
||||
return result
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
address_print_accumulated_stats :: proc "contextless" () {
|
||||
when ASAN_ENABLED {
|
||||
__asan_print_accumulated_stats()
|
||||
}
|
||||
}
|
||||
|
||||
address_get_current_fake_stack :: proc "contextless" () -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_current_fake_stack()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
address_is_in_fake_stack :: proc "contextless" (fake_stack: rawptr, addr: rawptr) -> ([]byte, bool) {
|
||||
when ASAN_ENABLED {
|
||||
begin: rawptr
|
||||
end: rawptr
|
||||
if __asan_addr_is_in_fake_stack(fake_stack, addr, &begin, &end) == nil {
|
||||
return {}, false
|
||||
}
|
||||
return ((cast([^]byte)begin)[:uintptr(end)-uintptr(begin)]), true
|
||||
} else {
|
||||
return {}, false
|
||||
}
|
||||
}
|
||||
|
||||
address_handle_no_return :: proc "contextless" () {
|
||||
when ASAN_ENABLED {
|
||||
__asan_handle_no_return()
|
||||
}
|
||||
}
|
||||
|
||||
address_update_allocation_context :: proc "contextless" (addr: rawptr) -> bool {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_update_allocation_context(addr) != 0
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
+43
-10
@@ -2,6 +2,7 @@ package mem
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "base:sanitizer"
|
||||
|
||||
/*
|
||||
Nil allocator.
|
||||
@@ -138,6 +139,7 @@ arena_init :: proc(a: ^Arena, data: []byte) {
|
||||
a.offset = 0
|
||||
a.peak_used = 0
|
||||
a.temp_count = 0
|
||||
sanitizer.address_poison(a.data)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -224,7 +226,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)
|
||||
sanitizer.address_unpoison(result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -232,6 +236,7 @@ Free all memory to an arena.
|
||||
*/
|
||||
arena_free_all :: proc(a: ^Arena) {
|
||||
a.offset = 0
|
||||
sanitizer.address_poison(a.data)
|
||||
}
|
||||
|
||||
arena_allocator_proc :: proc(
|
||||
@@ -309,6 +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)
|
||||
sanitizer.address_poison(tmp.arena.data[tmp.prev_offset:tmp.arena.offset])
|
||||
tmp.arena.offset = tmp.prev_offset
|
||||
tmp.arena.temp_count -= 1
|
||||
}
|
||||
@@ -363,6 +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
|
||||
sanitizer.address_poison(s.data)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -377,6 +384,7 @@ scratch_destroy :: proc(s: ^Scratch) {
|
||||
free_bytes(ptr, s.backup_allocator)
|
||||
}
|
||||
delete(s.leaked_allocations)
|
||||
sanitizer.address_unpoison(s.data)
|
||||
delete(s.data, s.backup_allocator)
|
||||
s^ = {}
|
||||
}
|
||||
@@ -472,7 +480,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)
|
||||
sanitizer.address_unpoison(result)
|
||||
return result, nil
|
||||
} else {
|
||||
a := s.backup_allocator
|
||||
if a.procedure == nil {
|
||||
@@ -516,6 +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)
|
||||
sanitizer.address_poison(s.data[s.curr_offset:])
|
||||
s.prev_allocation = nil
|
||||
return nil
|
||||
}
|
||||
@@ -546,6 +557,7 @@ scratch_free_all :: proc(s: ^Scratch, loc := #caller_location) {
|
||||
free_bytes(ptr, s.backup_allocator, loc)
|
||||
}
|
||||
clear(&s.leaked_allocations)
|
||||
sanitizer.address_poison(s.data)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -675,7 +687,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)
|
||||
sanitizer.address_unpoison(result)
|
||||
return result, nil
|
||||
}
|
||||
data, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
|
||||
if err != nil {
|
||||
@@ -776,6 +790,7 @@ stack_init :: proc(s: ^Stack, data: []byte) {
|
||||
s.prev_offset = 0
|
||||
s.curr_offset = 0
|
||||
s.peak_used = 0
|
||||
sanitizer.address_poison(data)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -861,15 +876,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))
|
||||
sanitizer.address_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)
|
||||
sanitizer.address_unpoison(result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -902,12 +921,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
|
||||
sanitizer.address_poison(s.data[old_offset:s.curr_offset])
|
||||
s.curr_offset = old_offset
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -917,6 +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
|
||||
sanitizer.address_poison(s.data)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1076,7 +1099,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)
|
||||
sanitizer.address_unpoison(result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
stack_allocator_proc :: proc(
|
||||
@@ -1144,6 +1169,7 @@ small_stack_init :: proc(s: ^Small_Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.offset = 0
|
||||
s.peak_used = 0
|
||||
sanitizer.address_poison(data)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1252,10 +1278,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))
|
||||
sanitizer.address_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)
|
||||
sanitizer.address_unpoison(result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1289,6 +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)))
|
||||
sanitizer.address_poison(s.data[old_offset:s.offset])
|
||||
s.offset = old_offset
|
||||
return nil
|
||||
}
|
||||
@@ -1298,6 +1328,7 @@ Free all memory to small stack.
|
||||
*/
|
||||
small_stack_free_all :: proc(s: ^Small_Stack) {
|
||||
s.offset = 0
|
||||
sanitizer.address_poison(s.data)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1442,7 +1473,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)
|
||||
sanitizer.address_unpoison(result)
|
||||
return result, nil
|
||||
}
|
||||
data, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
|
||||
if err == nil {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user