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
#define DQN_ASAN_POISON 1
#define DQN_ASAN_VET_POISON
#define DQN_NO_CHECK_BREAK
#define DQN_IMPLEMENTATION
#include "dqn.h"

17
dqn.h
View File

@ -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

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;
}
// 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);

View File

@ -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

View File

@ -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")

View File

@ -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;
}

View File

@ -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;
}

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);
#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) {