dqn: Put ASAN poisoning behind macros to allow vetting

This commit is contained in:
doyle 2023-08-29 00:52:01 +10:00
parent 2740987956
commit 0872da026a
8 changed files with 74 additions and 28 deletions

View File

@ -15,6 +15,7 @@
#endif #endif
#define DQN_ASAN_POISON 1 #define DQN_ASAN_POISON 1
#define DQN_ASAN_VET_POISON
#define DQN_NO_CHECK_BREAK #define DQN_NO_CHECK_BREAK
#define DQN_IMPLEMENTATION #define DQN_IMPLEMENTATION
#include "dqn.h" #include "dqn.h"

17
dqn.h
View File

@ -230,6 +230,23 @@
// flag. // flag.
// //
// DQN_LEAK_TRACING // DQN_LEAK_TRACING
//
// - Define this macro to 1 to enable poisoning of memory from arenas when ASAN
// `-fsanitize=address` is enabled. Enabling this will detect memory overwrite
// by padding allocated before and after with poisoned memory which will raise
// a use-after-poison in ASAN on read/write. This is a no-op if the library is
// not compiled with ASAN.
//
// DQN_ASAN_POISON 1
//
// - Define this macro to enable sanity checks for manually poisoned memory in
// this library when ASAN `-fsanitize=address` is enabled. These sanity checks
// ensure that memory from arenas are correctly un/poisoned when pointers are
// allocated and returned to the memory arena's. This is a no-op if we are not
// compiled with ASAN or `DQN_ASAN_POISON` is not set to `1`.
//
// DQN_ASAN_VET_POISON
//
// NOTE: Dqn_Strings =============================================================================== // NOTE: Dqn_Strings ===============================================================================
// [$CSTR] Dqn_CString8 | | C-string helpers // [$CSTR] Dqn_CString8 | | C-string helpers

View File

@ -220,8 +220,7 @@ DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator,
header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetString.size; header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetString.size;
} }
// NOTE: Header padding // NOTE: Header padding ========================================================================
// =========================================================================
Dqn_usize header_padding = 0; Dqn_usize header_padding = 0;
{ {
DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0; DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0;
@ -229,8 +228,7 @@ DQN_API Dqn_String8 Dqn_Log_MakeString(Dqn_Allocator allocator,
header_padding = max_header_length - header_size_no_ansi_codes; header_padding = max_header_length - header_size_no_ansi_codes;
} }
// NOTE: Construct final log // NOTE: Construct final log ===================================================================
// =========================================================================
Dqn_String8 user_msg = Dqn_String8_InitFV(allocator, fmt, args); Dqn_String8 user_msg = Dqn_String8_InitFV(allocator, fmt, args);
Dqn_String8 result = Dqn_String8_Allocate(allocator, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No); Dqn_String8 result = Dqn_String8_Allocate(allocator, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No);
DQN_MEMCPY(result.data, header.data, header.size); DQN_MEMCPY(result.data, header.data, header.size);

View File

@ -38,6 +38,36 @@
#define DQN_ASAN_POISON_ALIGNMENT 8 #define DQN_ASAN_POISON_ALIGNMENT 8
#endif #endif
// NOTE: MSVC does not support the feature detection macro for instance so we
// compile it out
#if !defined(__has_feature)
#define __has_feature(x) 0
#endif
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
#include <sanitizer/asan_interface.h>
#if defined(DQN_ASAN_VET_POISON)
#define DQN_ASAN_POISON_MEMORY_REGION(ptr, size) \
do { \
__asan_poison_memory_region((ptr), (size)); \
DQN_ASSERT(__asan_address_is_poisoned((ptr))); \
DQN_ASSERT(__asan_address_is_poisoned((char *)(ptr) + ((size) - 1))); \
DQN_ASSERT(!__asan_address_is_poisoned((char *)(ptr) + (size))); \
} while (0)
#define DQN_ASAN_UNPOISON_MEMORY_REGION(ptr, size) \
do { \
__asan_unpoison_memory_region((ptr), (size)); \
DQN_ASSERT(__asan_region_is_poisoned((ptr), (size)) == 0); \
} while (0)
#else
#define DQN_ASAN_POISON_MEMORY_REGION(ptr, size) __asan_poison_memory_region(ptr, size)
#define DQN_ASAN_UNPOISON_MEMORY_REGION(ptr, size) __asan_unpoison_memory_region(ptr, size)
#endif
#else
#define DQN_ASAN_POISON_MEMORY_REGION(ptr, size) (void)(ptr); (void)(size)
#define DQN_ASAN_UNPOISON_MEMORY_REGION(ptr, size) (void)(ptr); (void)(size)
#endif
// NOTE: [$CALL] Dqn_CallSite ====================================================================== // NOTE: [$CALL] Dqn_CallSite ======================================================================
struct Dqn_CallSite struct Dqn_CallSite

View File

@ -10,8 +10,6 @@
#include "b_stacktrace.h" #include "b_stacktrace.h"
// NOTE: [$OS_H] OS Headers ======================================================================== // NOTE: [$OS_H] OS Headers ========================================================================
#include <sanitizer/asan_interface.h>
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
#pragma comment(lib, "bcrypt") #pragma comment(lib, "bcrypt")
#pragma comment(lib, "wininet") #pragma comment(lib, "wininet")

View File

@ -840,6 +840,17 @@ DQN_API Dqn_Library *Dqn_Library_Init()
} }
#endif #endif
// ============================================================================================
Dqn_Log_DebugF("Dqn Library initialised with features\n");
if (DQN_ASAN_POISON)
Dqn_Print_StdLnF(Dqn_PrintStd_Err, " - ASAN manual poisoning");
#if defined(DQN_ASAN_VET_POISON)
Dqn_Print_StdLnF(Dqn_PrintStd_Err, " - ASAN manual poisoning vetting");
#endif
return result; return result;
} }

View File

@ -1,6 +1,4 @@
// NOTE: [$ALLO] Dqn_Allocator ===================================================================== // NOTE: [$ALLO] Dqn_Allocator =====================================================================
#include <ios>
#include <sanitizer/asan_interface.h>
DQN_API void *Dqn_Allocator_Alloc(Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem) DQN_API void *Dqn_Allocator_Alloc(Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem)
{ {
void *result = NULL; void *result = NULL;
@ -237,8 +235,9 @@ DQN_API Dqn_MemBlock *Dqn_MemBlock_Init(Dqn_usize reserve, Dqn_usize commit, uin
if (DQN_ASAN_POISON) { // NOTE: Poison (guard page + entire block), we unpoison as we allocate if (DQN_ASAN_POISON) { // NOTE: Poison (guard page + entire block), we unpoison as we allocate
DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result->data, DQN_ASAN_POISON_ALIGNMENT)); DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result->data, DQN_ASAN_POISON_ALIGNMENT));
DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result->size, DQN_ASAN_POISON_ALIGNMENT)); DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result->size, DQN_ASAN_POISON_ALIGNMENT));
void *poison_ptr = DQN_CAST(void *)Dqn_AlignUpPowerOfTwo(DQN_CAST(char *)result + sizeof(Dqn_MemBlock), DQN_ASAN_POISON_ALIGNMENT); void *poison_ptr = DQN_CAST(void *)Dqn_AlignUpPowerOfTwo(DQN_CAST(char *)result + sizeof(Dqn_MemBlock), DQN_ASAN_POISON_ALIGNMENT);
ASAN_POISON_MEMORY_REGION(poison_ptr, g_dqn_library->os_page_size + result->size); Dqn_usize bytes_to_poison = g_dqn_library->os_page_size + result->size;
DQN_ASAN_POISON_MEMORY_REGION(poison_ptr, bytes_to_poison);
} }
} }
return result; return result;
@ -261,9 +260,8 @@ DQN_API void *Dqn_MemBlock_Alloc(Dqn_MemBlock *block, Dqn_usize size, uint8_t al
block->used = new_used; block->used = new_used;
DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result, alignment)); DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result, alignment));
if (DQN_ASAN_POISON) { if (DQN_ASAN_POISON)
ASAN_UNPOISON_MEMORY_REGION(result, size); DQN_ASAN_UNPOISON_MEMORY_REGION(result, size);
}
if (zero_mem == Dqn_ZeroMem_Yes) { if (zero_mem == Dqn_ZeroMem_Yes) {
Dqn_usize reused_bytes = DQN_MIN(block->commit - size_required.data_offset, size); Dqn_usize reused_bytes = DQN_MIN(block->commit - size_required.data_offset, size);
@ -286,9 +284,8 @@ DQN_API void Dqn_MemBlock_Free(Dqn_MemBlock *block)
if (!block) if (!block)
return; return;
Dqn_usize release_size = block->size + Dqn_MemBlock_MetadataSize(); Dqn_usize release_size = block->size + Dqn_MemBlock_MetadataSize();
if (DQN_ASAN_POISON) { if (DQN_ASAN_POISON)
ASAN_UNPOISON_MEMORY_REGION(block, release_size); DQN_ASAN_UNPOISON_MEMORY_REGION(block, release_size);
}
Dqn_VMem_Release(block, release_size); Dqn_VMem_Release(block, release_size);
} }
@ -307,13 +304,10 @@ DQN_API void Dqn_MemBlock_PopTo(Dqn_MemBlock *block, Dqn_usize to)
return; return;
if (DQN_ASAN_POISON) { if (DQN_ASAN_POISON) {
// TODO(doyle): The poison API takes addresses that are 8 byte aligned void *poison_ptr = DQN_CAST(char *)block->data + to;
// so there are gaps here if we are dealing with objects that aren't 8 void *end_ptr = DQN_CAST(void *)Dqn_AlignUpPowerOfTwo((DQN_CAST(uintptr_t)block->data + block->used), DQN_ASAN_POISON_ALIGNMENT);
// byte aligned unfortunately.
void *poison_ptr = DQN_CAST(void *)Dqn_AlignUpPowerOfTwo(DQN_CAST(char *)block->data + to, DQN_ASAN_POISON_ALIGNMENT);
void *end_ptr = DQN_CAST(char *)block->data + block->used;
uintptr_t bytes_to_poison = DQN_CAST(uintptr_t)end_ptr - DQN_CAST(uintptr_t)poison_ptr; uintptr_t bytes_to_poison = DQN_CAST(uintptr_t)end_ptr - DQN_CAST(uintptr_t)poison_ptr;
ASAN_POISON_MEMORY_REGION(poison_ptr, bytes_to_poison); DQN_ASAN_POISON_MEMORY_REGION(poison_ptr, bytes_to_poison);
} }
block->used = to; block->used = to;
} }

View File

@ -31,8 +31,7 @@ DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_String8 string)
DQN_ASSERT(std_handle == Dqn_PrintStd_Out || std_handle == Dqn_PrintStd_Err); DQN_ASSERT(std_handle == Dqn_PrintStd_Out || std_handle == Dqn_PrintStd_Err);
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
// NOTE: Get the output handles from kernel // NOTE: Get the output handles from kernel ====================================================
// =========================================================================
DQN_THREAD_LOCAL void *std_out_print_handle = nullptr; DQN_THREAD_LOCAL void *std_out_print_handle = nullptr;
DQN_THREAD_LOCAL void *std_err_print_handle = nullptr; DQN_THREAD_LOCAL void *std_err_print_handle = nullptr;
DQN_THREAD_LOCAL bool std_out_print_to_console = false; DQN_THREAD_LOCAL bool std_out_print_to_console = false;
@ -47,8 +46,7 @@ DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_String8 string)
std_err_print_to_console = GetConsoleMode(std_err_print_handle, &mode) != 0; std_err_print_to_console = GetConsoleMode(std_err_print_handle, &mode) != 0;
} }
// NOTE: Select the output handle // NOTE: Select the output handle ==============================================================
// =========================================================================
void *print_handle = std_out_print_handle; void *print_handle = std_out_print_handle;
bool print_to_console = std_out_print_to_console; bool print_to_console = std_out_print_to_console;
if (std_handle == Dqn_PrintStd_Err) { if (std_handle == Dqn_PrintStd_Err) {
@ -56,8 +54,7 @@ DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_String8 string)
print_to_console = std_err_print_to_console; print_to_console = std_err_print_to_console;
} }
// NOTE: Write the string // NOTE: Write the string ======================================================================
// =========================================================================
DQN_ASSERT(string.size < DQN_CAST(unsigned long)-1); DQN_ASSERT(string.size < DQN_CAST(unsigned long)-1);
unsigned long bytes_written = 0; (void)bytes_written; unsigned long bytes_written = 0; (void)bytes_written;
if (print_to_console) { if (print_to_console) {