dqn: Put ASAN poisoning behind macros to allow vetting
This commit is contained in:
parent
2740987956
commit
0872da026a
@ -15,6 +15,7 @@
|
||||
#endif
|
||||
|
||||
#define DQN_ASAN_POISON 1
|
||||
#define DQN_ASAN_VET_POISON
|
||||
#define DQN_NO_CHECK_BREAK
|
||||
#define DQN_IMPLEMENTATION
|
||||
#include "dqn.h"
|
||||
|
17
dqn.h
17
dqn.h
@ -230,6 +230,23 @@
|
||||
// flag.
|
||||
//
|
||||
// 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 ===============================================================================
|
||||
// [$CSTR] Dqn_CString8 | | C-string helpers
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
// NOTE: Header padding
|
||||
// =========================================================================
|
||||
// NOTE: Header padding ========================================================================
|
||||
Dqn_usize header_padding = 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;
|
||||
}
|
||||
|
||||
// NOTE: Construct final log
|
||||
// =========================================================================
|
||||
// NOTE: Construct final log ===================================================================
|
||||
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_MEMCPY(result.data, header.data, header.size);
|
||||
|
30
dqn_debug.h
30
dqn_debug.h
@ -38,6 +38,36 @@
|
||||
#define DQN_ASAN_POISON_ALIGNMENT 8
|
||||
#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 ======================================================================
|
||||
struct Dqn_CallSite
|
||||
|
@ -10,8 +10,6 @@
|
||||
#include "b_stacktrace.h"
|
||||
|
||||
// NOTE: [$OS_H] OS Headers ========================================================================
|
||||
#include <sanitizer/asan_interface.h>
|
||||
|
||||
#if defined(DQN_OS_WIN32)
|
||||
#pragma comment(lib, "bcrypt")
|
||||
#pragma comment(lib, "wininet")
|
||||
|
@ -840,6 +840,17 @@ DQN_API Dqn_Library *Dqn_Library_Init()
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
// 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)
|
||||
{
|
||||
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
|
||||
DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result->data, 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);
|
||||
ASAN_POISON_MEMORY_REGION(poison_ptr, g_dqn_library->os_page_size + result->size);
|
||||
void *poison_ptr = DQN_CAST(void *)Dqn_AlignUpPowerOfTwo(DQN_CAST(char *)result + sizeof(Dqn_MemBlock), DQN_ASAN_POISON_ALIGNMENT);
|
||||
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;
|
||||
@ -261,9 +260,8 @@ DQN_API void *Dqn_MemBlock_Alloc(Dqn_MemBlock *block, Dqn_usize size, uint8_t al
|
||||
block->used = new_used;
|
||||
DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result, alignment));
|
||||
|
||||
if (DQN_ASAN_POISON) {
|
||||
ASAN_UNPOISON_MEMORY_REGION(result, size);
|
||||
}
|
||||
if (DQN_ASAN_POISON)
|
||||
DQN_ASAN_UNPOISON_MEMORY_REGION(result, size);
|
||||
|
||||
if (zero_mem == Dqn_ZeroMem_Yes) {
|
||||
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)
|
||||
return;
|
||||
Dqn_usize release_size = block->size + Dqn_MemBlock_MetadataSize();
|
||||
if (DQN_ASAN_POISON) {
|
||||
ASAN_UNPOISON_MEMORY_REGION(block, release_size);
|
||||
}
|
||||
if (DQN_ASAN_POISON)
|
||||
DQN_ASAN_UNPOISON_MEMORY_REGION(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;
|
||||
|
||||
if (DQN_ASAN_POISON) {
|
||||
// TODO(doyle): The poison API takes addresses that are 8 byte aligned
|
||||
// so there are gaps here if we are dealing with objects that aren't 8
|
||||
// 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;
|
||||
void *poison_ptr = DQN_CAST(char *)block->data + to;
|
||||
void *end_ptr = DQN_CAST(void *)Dqn_AlignUpPowerOfTwo((DQN_CAST(uintptr_t)block->data + block->used), DQN_ASAN_POISON_ALIGNMENT);
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
#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_err_print_handle = nullptr;
|
||||
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;
|
||||
}
|
||||
|
||||
// NOTE: Select the output handle
|
||||
// =========================================================================
|
||||
// NOTE: Select the output handle ==============================================================
|
||||
void *print_handle = std_out_print_handle;
|
||||
bool print_to_console = std_out_print_to_console;
|
||||
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;
|
||||
}
|
||||
|
||||
// NOTE: Write the string
|
||||
// =========================================================================
|
||||
// NOTE: Write the string ======================================================================
|
||||
DQN_ASSERT(string.size < DQN_CAST(unsigned long)-1);
|
||||
unsigned long bytes_written = 0; (void)bytes_written;
|
||||
if (print_to_console) {
|
||||
|
Loading…
Reference in New Issue
Block a user