Last active
April 16, 2020 20:08
Revisions
-
kevinw revised this gist
Apr 16, 2020 . 1 changed file with 18 additions and 10 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,7 +2,6 @@ package debug_alloc import "core:mem" import "core:os" import "core:sys/win32" guard_allocator := mem.Allocator { @@ -31,11 +30,16 @@ guard_allocator_proc :: proc( switch mode { case .Alloc: page_size := os.get_page_size(); // Here we save room for an (addr, size) pair allocated before // the user's block of memory, so we can free it later. actual_size := (size_of(uintptr)*2) + size; // we ask VirtualAlloc for 2 pages before and after our user block num_pages_needed := ((actual_size - 1) / page_size) + 1; valloc_size := (2 + num_pages_needed) * page_size; block := cast(^byte)virtual_alloc(nil, cast(uint)valloc_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); when #defined(TEST_GUARD_ALLOC) do fmt.println("virtual_alloc(", block, ",", valloc_size, ")"); assert(block != nil, "virtual alloc failed"); @@ -51,7 +55,7 @@ guard_allocator_proc :: proc( // We store the original virtual_alloc result and size just before the returned block memory. (cast(^int)mem.ptr_offset(ptr, -size_of(int)*2))^ = cast(int)cast(uintptr)block; (cast(^int)mem.ptr_offset(ptr, -size_of(int)*1))^ = valloc_size; return ptr; case .Free: @@ -80,15 +84,19 @@ when #defined(TEST_GUARD_ALLOC) { main :: proc() { context.allocator = guard_allocator; N :: 5000; rptr := mem.alloc(N); ptr := cast(^byte)rptr; ptr_to_valid_memory := mem.ptr_offset(ptr, N-1); os.write_string(os.stdout, "writing to valid memory..."); ptr_to_valid_memory^ = 0; os.write_string(os.stdout, "...it worked!\n"); ptr_to_invalid_memory := mem.ptr_offset(ptr, N); os.write_string(os.stdout, "about to crash..."); ptr_to_invalid_memory^ = 0; mem.free(rptr); os.write_string(os.stdout, "...success! (you shouldn't see this!)"); } } -
kevinw created this gist
Apr 16, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,94 @@ package debug_alloc import "core:mem" import "core:os" import "core:math" import "core:sys/win32" guard_allocator := mem.Allocator { procedure = guard_allocator_proc, data = nil, }; guard_allocator_proc :: proc( allocator_data: rawptr, mode: mem.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr { when ODIN_OS == "windows" { check_result :: proc(res: win32.Bool) { zero : win32.Bool; assert(res != zero, "virtual_protect failed"); } using win32; switch mode { case .Alloc: page_size := os.get_page_size(); actual_size := (size_of(uintptr)*2) + size; num_pages_needed := cast(int)math.ceil(cast(f32)actual_size / cast(f32)page_size); size_per_alloc := (2 + num_pages_needed) * page_size; block := cast(^byte)virtual_alloc(nil, cast(uint)size_per_alloc, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); when #defined(TEST_GUARD_ALLOC) do fmt.println("virtual_alloc(", block, ",", size_per_alloc, ")"); assert(block != nil, "virtual alloc failed"); before_page := block; after_page := mem.ptr_offset(before_page, cast(int)page_size * (1 + num_pages_needed)); old_protect: u32; new_protect :u32 = PAGE_GUARD | PAGE_READONLY; check_result(virtual_protect(before_page, cast(uint)page_size, new_protect, &old_protect)); check_result(virtual_protect(after_page, cast(uint)page_size, new_protect, &old_protect)); ptr := mem.ptr_offset(after_page, -size); // We store the original virtual_alloc result and size just before the returned block memory. (cast(^int)mem.ptr_offset(ptr, -size_of(int)*2))^ = cast(int)cast(uintptr)block; (cast(^int)mem.ptr_offset(ptr, -size_of(int)*1))^ = size_per_alloc; return ptr; case .Free: orig_alloc_size := (cast(^int)mem.ptr_offset(cast(^byte)old_memory, -size_of(int)))^; orig_alloc_address := (cast(^rawptr)cast(uintptr)mem.ptr_offset(cast(^byte)old_memory, -size_of(int)*2))^; when #defined(TEST_GUARD_ALLOC) do fmt.println("virtual_free(", orig_alloc_address, ",", orig_alloc_size, ")"); virtual_free(orig_alloc_address, cast(uint)orig_alloc_size, MEM_RELEASE); case .Free_All: assert(false, "unimplemented"); case .Resize: return mem.default_resize_align(old_memory, old_size, size, alignment, context.allocator, location); } } else { #panic("guard_allocator is unimplemented for this OS"); return nil; } return nil; } when #defined(TEST_GUARD_ALLOC) { import "core:fmt" main :: proc() { context.allocator = guard_allocator; N :: 50000; rptr := mem.alloc(N); ptr := cast(^byte)rptr; ptr_to_invalid_memory := mem.ptr_offset(ptr, N); //fmt.println("about to write to", ptr_to_invalid_memory); os.write_string(os.stdout, "about to crash..."); ptr_to_invalid_memory^ = 0; mem.free(rptr); os.write_string(os.stdout, "success (you shouldn't see this!)"); } }