Fix memstack free not clearing metadata
This commit is contained in:
		
							parent
							
								
									6a02e38706
								
							
						
					
					
						commit
						7228a8b344
					
				
							
								
								
									
										90
									
								
								dqn.h
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								dqn.h
									
									
									
									
									
								
							| @ -345,14 +345,21 @@ class DqnSmartArray : public DqnArray<T> | |||||||
| // #DqnAllocatorMetadata
 | // #DqnAllocatorMetadata
 | ||||||
| // =================================================================================================
 | // =================================================================================================
 | ||||||
| // Allocation Layout
 | // Allocation Layout
 | ||||||
| // +----------------------------------------------------------------------------------------------------------------------------+
 | //                      +------------------------------------------------------------+                        +-----------------+
 | ||||||
| // |                     | Allocation Head                                           |                        | Allocation Tail |
 | //                      | Allocation Head                                            |                        | Allocation Tail |
 | ||||||
| // +----------------------------------------------------------------------------------------------------------------------------+
 | // +--------------------+------------------------------------------------------------+------------------------+-----------------+
 | ||||||
| // | Ptr From Allocator | Offset To Src | Alignment | Alloc Amount | B. Guard (Opt.) | Aligned Ptr For Client | B. Guard (Opt.) |
 | // | Ptr From Allocator | Offset To Src | Alignment | Alloc Amount | B. Guard (Opt.) | Aligned Ptr For Client | B. Guard (Opt.) |
 | ||||||
| // +----------------------------------------------------------------------------------------------------------------------------+
 | // +----------------------------------------------------------------------------------------------------------------------------+
 | ||||||
|  | // Ptr From Allocator:     The pointer returned by the allocator, not aligned
 | ||||||
|  | // Alignment:              The pointer given to the client is aligned to this power of two boundary
 | ||||||
|  | // Alloc Amount:           The requested allocation amount by the client (so does not include metadata)
 | ||||||
|  | // B. Guard:               Bounds Guard value.
 | ||||||
|  | // Aligned Ptr For Client: The allocated memory for the client.
 | ||||||
|  | // Offset To Src:          Number of bytes to subtract from the "Aligned Ptr For Client" to return to the "Ptr From ALlocator"
 | ||||||
| struct DqnAllocatorMetadata | struct DqnAllocatorMetadata | ||||||
| { | { | ||||||
| 	static u32 const GUARD_VALUE        = 0xBEBAFECA; | 	static u32 const HEAD_GUARD_VALUE        = 0xCAFEBABE; | ||||||
|  | 	static u32 const TAIL_GUARD_VALUE        = 0xDEADBEEF; | ||||||
| 	static u32 const OFFSET_TO_SRC_SIZE = sizeof(u8); | 	static u32 const OFFSET_TO_SRC_SIZE = sizeof(u8); | ||||||
| 	static u32 const ALIGNMENT_SIZE     = sizeof(u8); | 	static u32 const ALIGNMENT_SIZE     = sizeof(u8); | ||||||
| 	static u32 const ALLOC_AMOUNT_SIZE  = sizeof(usize); | 	static u32 const ALLOC_AMOUNT_SIZE  = sizeof(usize); | ||||||
| @ -369,12 +376,13 @@ struct DqnAllocatorMetadata | |||||||
| 	auto   GetAllocHeadSize    ()                         const { return allocHeadSize; } | 	auto   GetAllocHeadSize    ()                         const { return allocHeadSize; } | ||||||
| 	auto   GetAllocTailSize    ()                         const { return allocTailSize; } | 	auto   GetAllocTailSize    ()                         const { return allocTailSize; } | ||||||
| 
 | 
 | ||||||
|  | 	usize  GetAllocationSize   (usize size, u8 alignment) const { return GetAllocHeadSize() + size + GetAllocTailSize() + (alignment - 1); } | ||||||
|  | 
 | ||||||
| 	u32   *PtrToHeadBoundsGuard(u8 const *ptr)            const; // ptr: The ptr given to the client when allocating.
 | 	u32   *PtrToHeadBoundsGuard(u8 const *ptr)            const; // ptr: The ptr given to the client when allocating.
 | ||||||
| 	u32   *PtrToTailBoundsGuard(u8 const *ptr)            const; // IMPORTANT: Uses "Alloc Amount" metadata to find the tail!
 | 	u32   *PtrToTailBoundsGuard(u8 const *ptr)            const; // IMPORTANT: Uses "Alloc Amount" metadata to find the tail!
 | ||||||
| 	u8    *PtrToAlignment      (u8 const *ptr)            const; | 	u8    *PtrToAlignment      (u8 const *ptr)            const; | ||||||
| 	u8    *PtrToOffsetToSrc    (u8 const *ptr)            const; | 	u8    *PtrToOffsetToSrc    (u8 const *ptr)            const; | ||||||
| 	usize *PtrToAllocAmount    (u8 const *ptr)            const; | 	usize *PtrToAllocAmount    (u8 const *ptr)            const; | ||||||
| 	usize  GetAllocationSize   (usize size, u8 alignment) const { return GetAllocHeadSize() + size + GetAllocTailSize() + (alignment - 1); } |  | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	u32            boundsGuardSize; // sizeof(GUARD_VALUE) OR 0 if BoundsGuard is disabled.
 | 	u32            boundsGuardSize; // sizeof(GUARD_VALUE) OR 0 if BoundsGuard is disabled.
 | ||||||
| @ -404,7 +412,9 @@ struct DqnMemStack | |||||||
| 	enum Flag | 	enum Flag | ||||||
| 	{ | 	{ | ||||||
| 		NonExpandable       = (1 << 0), // Disallow additional memory blocks when full.
 | 		NonExpandable       = (1 << 0), // Disallow additional memory blocks when full.
 | ||||||
| 		BoundsGuard   = (1 << 1), // Track, check and add guards on all allocations
 | 		NonExpandableAssert = (1 << 1), // Assert when NonExpandable is set with allocation on a full stack.
 | ||||||
|  | 		BoundsGuard         = (1 << 2), // Track, check and add guards on all allocations
 | ||||||
|  | 		All                 = (NonExpandable | NonExpandableAssert | BoundsGuard), | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	struct Info // Statistics of the memory stack.
 | 	struct Info // Statistics of the memory stack.
 | ||||||
| @ -437,7 +447,7 @@ struct DqnMemStack | |||||||
| 	// Uses fixed buffer, allocations will be sourced from the buffer and fail after buffer is full.
 | 	// Uses fixed buffer, allocations will be sourced from the buffer and fail after buffer is full.
 | ||||||
| 	// mem:       Memory to use for the memory stack
 | 	// mem:       Memory to use for the memory stack
 | ||||||
| 	// return:    FALSE if args are invalid, or insufficient memSize.
 | 	// return:    FALSE if args are invalid, or insufficient memSize.
 | ||||||
| 	bool Init(u8 *const mem, usize size, u32 flags_ = 0); | 	bool Init(void *const mem, usize size, bool zeroClear, u32 flags_ = 0); | ||||||
| 
 | 
 | ||||||
| 	// Memory Stack capable of expanding when full, but only if NonExpandable flag is not set.
 | 	// Memory Stack capable of expanding when full, but only if NonExpandable flag is not set.
 | ||||||
| 	bool Init(usize size, bool zeroClear, u32 flags_ = 0, DqnMemAPI *const api = &DQN_DEFAULT_HEAP_ALLOCATOR); | 	bool Init(usize size, bool zeroClear, u32 flags_ = 0, DqnMemAPI *const api = &DQN_DEFAULT_HEAP_ALLOCATOR); | ||||||
| @ -3255,10 +3265,12 @@ DqnMemAPI DqnMemAPI::StackAllocator(struct DqnMemStack *const stack) | |||||||
| // =================================================================================================
 | // =================================================================================================
 | ||||||
| void DqnAllocatorMetadata::Init(bool boundsGuard) | void DqnAllocatorMetadata::Init(bool boundsGuard) | ||||||
| { | { | ||||||
|  | 	// TODO(doyle): How to handle memory here.
 | ||||||
| 	if (boundsGuard) | 	if (boundsGuard) | ||||||
| 	{ | 	{ | ||||||
| 		this->boundsGuardSize = sizeof(GUARD_VALUE); | 		this->boundsGuardSize        = sizeof(HEAD_GUARD_VALUE); | ||||||
| 		DQN_ASSERT(this->allocations.InitSize(128)); | 		LOCAL_PERSIST DqnMemAPI heap = DqnMemAPI::HeapAllocator(); | ||||||
|  | 		DQN_ASSERT(this->allocations.InitSize(128, &heap)); | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| @ -3292,20 +3304,22 @@ void DqnAllocatorMetadata::RemoveAllocation(u8 *ptr) | |||||||
| 
 | 
 | ||||||
| void DqnAllocatorMetadata::CheckAllocations() const | void DqnAllocatorMetadata::CheckAllocations() const | ||||||
| { | { | ||||||
| 	for (u8 const *ptr : this->allocations) | 	for (auto index = 0; index < this->allocations.count; index++) | ||||||
| 	{ | 	{ | ||||||
|  | 		u8 const *ptr = this->allocations.data[index]; | ||||||
|  | 
 | ||||||
| 		u32 const *headGuard = this->PtrToHeadBoundsGuard(ptr); | 		u32 const *headGuard = this->PtrToHeadBoundsGuard(ptr); | ||||||
| 		u32 const *tailGuard = this->PtrToTailBoundsGuard(ptr); | 		u32 const *tailGuard = this->PtrToTailBoundsGuard(ptr); | ||||||
| 
 | 
 | ||||||
| 		DQN_ASSERTM(*headGuard == GUARD_VALUE, | 		DQN_ASSERTM(*headGuard == HEAD_GUARD_VALUE, | ||||||
| 		            "Bounds guard has been destroyed at the head end of the allocation! Expected: " | 		            "Bounds guard has been destroyed at the head end of the allocation! Expected: " | ||||||
| 		            "%x, received: %x", | 		            "%x, received: %x", | ||||||
| 		            GUARD_VALUE, *headGuard); | 		            HEAD_GUARD_VALUE, *headGuard); | ||||||
| 
 | 
 | ||||||
| 		DQN_ASSERTM(*tailGuard == GUARD_VALUE, | 		DQN_ASSERTM(*tailGuard == TAIL_GUARD_VALUE, | ||||||
| 		            "Bounds guard has been destroyed at the tail end of the allocation! Expected: " | 		            "Bounds guard has been destroyed at the tail end of the allocation! Expected: " | ||||||
| 		            "%x, received: %x", | 		            "%x, received: %x", | ||||||
| 		            GUARD_VALUE, *tailGuard); | 		            TAIL_GUARD_VALUE, *tailGuard); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -3388,7 +3402,7 @@ DQN_FILE_SCOPE DqnMemStack::Block *DqnMemStackInternal_AllocateBlock(usize size, | |||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool DqnMemStack::Init(u8 *const mem, usize size, u32 flags_) | bool DqnMemStack::Init(void *const mem, usize size, bool zeroClear, u32 flags_) | ||||||
| { | { | ||||||
| 	if (!mem) | 	if (!mem) | ||||||
| 	{ | 	{ | ||||||
| @ -3405,8 +3419,11 @@ bool DqnMemStack::Init(u8 *const mem, usize size, u32 flags_) | |||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if (zeroClear) | ||||||
|  | 		DqnMem_Set(mem, 0, size); | ||||||
|  | 
 | ||||||
| 	this->block            = (DqnMemStack::Block *)mem; | 	this->block            = (DqnMemStack::Block *)mem; | ||||||
| 	this->block->memory    = mem + sizeof(DqnMemStack::Block); | 	this->block->memory    = (u8 *)mem + sizeof(DqnMemStack::Block); | ||||||
| 	this->block->used      = 0; | 	this->block->used      = 0; | ||||||
| 	this->block->size      = size - sizeof(DqnMemStack::Block); | 	this->block->size      = size - sizeof(DqnMemStack::Block); | ||||||
| 	this->block->prevBlock = nullptr; | 	this->block->prevBlock = nullptr; | ||||||
| @ -3488,6 +3505,9 @@ void *DqnMemStack::Push(usize size, u8 alignment) | |||||||
| 		if (Dqn_BitIsSet(this->flags, Flag::NonExpandable)) | 		if (Dqn_BitIsSet(this->flags, Flag::NonExpandable)) | ||||||
| 		{ | 		{ | ||||||
| 			DQN_LOGE("Allocator is non-expandable and has run out of memory."); | 			DQN_LOGE("Allocator is non-expandable and has run out of memory."); | ||||||
|  | 			if (Dqn_BitIsSet(this->flags, Flag::NonExpandableAssert)) | ||||||
|  | 				DQN_ASSERT(DQN_INVALID_CODE_PATH); | ||||||
|  | 
 | ||||||
| 			return nullptr; | 			return nullptr; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| @ -3532,8 +3552,8 @@ void *DqnMemStack::Push(usize size, u8 alignment) | |||||||
| 		{ | 		{ | ||||||
| 			auto *headGuard = myMetadata->PtrToHeadBoundsGuard(result); | 			auto *headGuard = myMetadata->PtrToHeadBoundsGuard(result); | ||||||
| 			auto *tailGuard = myMetadata->PtrToTailBoundsGuard(result); | 			auto *tailGuard = myMetadata->PtrToTailBoundsGuard(result); | ||||||
| 			*headGuard       = DqnAllocatorMetadata::GUARD_VALUE; | 			*headGuard       = DqnAllocatorMetadata::HEAD_GUARD_VALUE; | ||||||
| 			*tailGuard       = DqnAllocatorMetadata::GUARD_VALUE; | 			*tailGuard       = DqnAllocatorMetadata::TAIL_GUARD_VALUE; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -3555,6 +3575,22 @@ void *DqnMemStack::Push(usize size, u8 alignment) | |||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | FILE_SCOPE void DqnMemStackInternal_KillMetadataPtrsExistingInBlock(DqnAllocatorMetadata *metadata, | ||||||
|  |                                                                     DqnMemStack::Block const *block) | ||||||
|  | { | ||||||
|  | 	u8 const *blockStart = block->memory; | ||||||
|  | 	u8 const *blockEnd   = block->memory + block->size; | ||||||
|  | 	for (auto index = 0; index < metadata->allocations.count; index++) | ||||||
|  | 	{ | ||||||
|  | 		u8 *ptr = metadata->allocations.data[index]; | ||||||
|  | 		if (ptr >= blockStart && ptr <= blockEnd) | ||||||
|  | 		{ | ||||||
|  | 			metadata->allocations.RemoveStable(index); | ||||||
|  | 			index--; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void DqnMemStack::Pop(void *const ptr, bool zeroClear) | void DqnMemStack::Pop(void *const ptr, bool zeroClear) | ||||||
| { | { | ||||||
| 	if (!ptr) return; | 	if (!ptr) return; | ||||||
| @ -3622,17 +3658,7 @@ bool DqnMemStack::FreeMemBlock(DqnMemStack::Block *memBlock) | |||||||
| 
 | 
 | ||||||
| 		if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard)) | 		if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard)) | ||||||
| 		{ | 		{ | ||||||
| 			u8 const *blockStart = blockToFree->memory; | 			DqnMemStackInternal_KillMetadataPtrsExistingInBlock(&this->metadata, blockToFree); | ||||||
| 			u8 const *blockEnd   = blockToFree->memory + blockToFree->size; |  | ||||||
| 			for (auto index = 0; index < this->metadata.allocations.count; index++) |  | ||||||
| 			{ |  | ||||||
| 				u8 *ptr = this->metadata.allocations.data[index]; |  | ||||||
| 				if (ptr >= blockStart && ptr <= blockEnd) |  | ||||||
| 				{ |  | ||||||
| 					this->metadata.allocations.RemoveStable(index); |  | ||||||
| 					index--; |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		usize realSize = blockToFree->size + sizeof(DqnMemStack::Block); | 		usize realSize = blockToFree->size + sizeof(DqnMemStack::Block); | ||||||
| @ -3656,11 +3682,17 @@ void DqnMemStack::ClearCurrBlock(bool zeroClear) | |||||||
| { | { | ||||||
| 	if (this->block) | 	if (this->block) | ||||||
| 	{ | 	{ | ||||||
|  | 		if (Dqn_BitIsSet(this->flags, Flag::BoundsGuard)) | ||||||
|  | 		{ | ||||||
|  | 			DqnMemStackInternal_KillMetadataPtrsExistingInBlock(&this->metadata, this->block); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		this->block->used = 0; | 		this->block->used = 0; | ||||||
| 		if (zeroClear) | 		if (zeroClear) | ||||||
| 		{ | 		{ | ||||||
| 			DqnMem_Clear(this->block->memory, 0, this->block->size); | 			DqnMem_Clear(this->block->memory, 0, this->block->size); | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2493,7 +2493,7 @@ FILE_SCOPE void DqnMemStack_Test() | |||||||
| 		{ | 		{ | ||||||
| 			u8 memBuf[sizeof(DqnMemStack::Block) - 1] = {}; | 			u8 memBuf[sizeof(DqnMemStack::Block) - 1] = {}; | ||||||
| 			DqnMemStack stack                         = {}; | 			DqnMemStack stack                         = {}; | ||||||
| 			auto result = stack.Init(&(memBuf[0]), DQN_ARRAY_COUNT(memBuf)); | 			auto result = stack.Init(&(memBuf[0]), DQN_ARRAY_COUNT(memBuf), false); | ||||||
| 
 | 
 | ||||||
| 			DQN_ASSERT(result == false); | 			DQN_ASSERT(result == false); | ||||||
| 			DQN_ASSERT(stack.block == nullptr); | 			DQN_ASSERT(stack.block == nullptr); | ||||||
| @ -2506,7 +2506,7 @@ FILE_SCOPE void DqnMemStack_Test() | |||||||
| 			i32 const bufSize  = sizeof(DqnMemStack::Block) * 5; | 			i32 const bufSize  = sizeof(DqnMemStack::Block) * 5; | ||||||
| 			u8 memBuf[bufSize] = {}; | 			u8 memBuf[bufSize] = {}; | ||||||
| 			DqnMemStack stack  = {}; | 			DqnMemStack stack  = {}; | ||||||
| 			auto result        = stack.Init(memBuf, bufSize); | 			auto result        = stack.Init(&(memBuf[0]), bufSize, false); | ||||||
| 
 | 
 | ||||||
| 			DQN_ASSERT(result == true); | 			DQN_ASSERT(result == true); | ||||||
| 			DQN_ASSERT(stack.block); | 			DQN_ASSERT(stack.block); | ||||||
| @ -2594,8 +2594,8 @@ FILE_SCOPE void DqnMemStack_Test() | |||||||
| 		// TODO(doyle): check head and tail are adjacent to the bounds of the allocation
 | 		// TODO(doyle): check head and tail are adjacent to the bounds of the allocation
 | ||||||
| 		u32 *head = stack.metadata.PtrToHeadBoundsGuard((u8 *)result); | 		u32 *head = stack.metadata.PtrToHeadBoundsGuard((u8 *)result); | ||||||
| 		u32 *tail = stack.metadata.PtrToTailBoundsGuard((u8 *)result); | 		u32 *tail = stack.metadata.PtrToTailBoundsGuard((u8 *)result); | ||||||
| 		DQN_ASSERT(*head == DqnAllocatorMetadata::GUARD_VALUE); | 		DQN_ASSERT(*head == DqnAllocatorMetadata::HEAD_GUARD_VALUE); | ||||||
| 		DQN_ASSERT(*tail == DqnAllocatorMetadata::GUARD_VALUE); | 		DQN_ASSERT(*tail == DqnAllocatorMetadata::TAIL_GUARD_VALUE); | ||||||
| 
 | 
 | ||||||
| 		Log(Status::Ok, "Bounds guards are placed adjacent and have magic values."); | 		Log(Status::Ok, "Bounds guards are placed adjacent and have magic values."); | ||||||
| 	} | 	} | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user