Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
MallocStomp2.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4#include "CoreTypes.h"
5#include "HAL/MemoryBase.h"
6#include "HAL/PlatformTLS.h"
7
8#ifndef WITH_MALLOC_STOMP2
9#define WITH_MALLOC_STOMP2 0
10#endif
12#include <atomic>
13/**
14 * Stomp memory allocator support should be enabled in Core.Build.cs.
15 * Run-time validation should be enabled using '-stomp2malloc' command line argument.
16 */
17
18#if PLATFORM_MICROSOFT
19 #include "Microsoft/WindowsHWrapper.h"
20#endif
21
22/**
23 * Stomp memory allocator. It helps find the following errors:
24 * - Read or writes off the end of an allocation.
25 * - Read or writes off the beginning of an allocation.
26 * - Read or writes after freeing an allocation.
27 */
28class FMallocStomp2 final : public FMalloc
29{
30private:
31 FORCEINLINE SIZE_T GetAlignedSize(SIZE_T InSize, SIZE_T InAlignment)
32 {
33 return (((InSize)+(InAlignment - 1)) & ~(InAlignment - 1));
34 }
35
36
37#if PLATFORM_64BITS
38 /** Expected value to be found in the sentinel. */
39 static constexpr SIZE_T SentinelExpectedValue = 0xdeadbeefdeadbeef;
40#else
41 /** Expected value to be found in the sentinel. */
42 static constexpr SIZE_T SentinelExpectedValue = 0xdeadbeef;
43#endif
44
45 const SIZE_T PageSize;
46
47 struct FAllocationData
48 {
49 /** Pointer to the full allocation. Needed so the OS knows what to free. */
50 void *FullAllocationPointer;
51 /** Full size of the allocation including the extra page. */
52 SIZE_T FullSize;
53 /** Size of the allocation requested. */
54 SIZE_T Size;
55 /** Sentinel used to check for underrun. */
56 SIZE_T Sentinel;
57 };
58
59public:
60 struct FAddressSpaceStompDataBlockRangeEntry
61 {
62 UPTRINT StartAddress;
63 UPTRINT EndAddress;
64 int StartIndex;
65 int EndIndex;
66 };
67
68
69
70 struct FAddressSpaceStompDataBlock
71 {
72 static constexpr SIZE_T VirtualAddressSpaceToReserve = 1024LL * 1024LL * 1024LL * 64LL; // 64 GB blocks, we try to reserve that much, but will half it and retry
73 std::atomic<UPTRINT> CurrentAddress;
74 UPTRINT StartAddress;
75 UPTRINT EndAddress;
76 SIZE_T Size;
77
78 bool Init()
79 {
80 SIZE_T SizeToTryAndReserve = VirtualAddressSpaceToReserve;
81 // if we can't reserve the full size, half the size and try again as long as we are over 1GB
82 while (SizeToTryAndReserve >= 1024LL * 1024LL * 1024LL)
83 {
84 StartAddress = (UPTRINT) ::VirtualAlloc(nullptr, SizeToTryAndReserve, MEM_RESERVE, PAGE_READWRITE);
85 if (StartAddress)
86 {
87 EndAddress = StartAddress + SizeToTryAndReserve;
88 Size = SizeToTryAndReserve;
89 CurrentAddress = StartAddress;
90 return true;
91 }
92 SizeToTryAndReserve /= 2;
93 }
94 return false;
95 }
96
97 void* Malloc(SIZE_T InSizeToAllocate, SIZE_T InPageSize, bool InUnderrun = false)
98 {
99 UPTRINT Ret = CurrentAddress.fetch_add(InSizeToAllocate);
100 if (Ret + InSizeToAllocate > EndAddress)
101 {
102 return nullptr;
103 }
104 if (!InUnderrun)
105 {
106 // leave the last page without backing store
107 Ret = (UPTRINT) ::VirtualAlloc((void*)Ret, InSizeToAllocate - InPageSize, MEM_COMMIT, PAGE_READWRITE);
108 }
109 else
110 {
111 // leave the first page without backing store
112 Ret = (UPTRINT) ::VirtualAlloc((void*)(Ret+InPageSize), InSizeToAllocate, MEM_COMMIT, PAGE_READWRITE);
113 }
114 return (void*)Ret;
115 }
116
117 void Free(void* InPtr, SIZE_T InSize)
118 {
119#if PLATFORM_WINDOWS && USING_CODE_ANALYSIS
120 MSVC_PRAGMA(warning(push))
121 MSVC_PRAGMA(warning(disable : 6250)) // Suppress C6250, as virtual address space is "leaked" by design.
122#endif
123 // Unmap physical memory, but keep virtual address range reserved to catch use-after-free errors.
124 ::VirtualFree(InPtr, InSize, MEM_DECOMMIT);
125
126#if PLATFORM_WINDOWS && USING_CODE_ANALYSIS
127 MSVC_PRAGMA(warning(pop))
128#endif
129 }
130 };
131
132
133private:
134 static constexpr SIZE_T TargetAddressSpaceToReserve = 1024LL * 1024LL * 1024LL * 1024LL * 32LL; // how much address space we will try and reserve, 32 TB
135
136
137 static constexpr SIZE_T MaxAddressSpaceStompDataBlocks = 4096; // this * VirtualAddressSpaceToReserve is how much address space we will try and allocate
138
139 std::atomic<int64> CurrentAddressSpaceStompDataBlockIndex;
140 FAddressSpaceStompDataBlock AddressSpaceStompDataBlocks[MaxAddressSpaceStompDataBlocks];
141
142 int NumberOfRangeEntries;
143 FAddressSpaceStompDataBlockRangeEntry AddressSpaceStompDataBlocksRangeEntries[MaxAddressSpaceStompDataBlocks];
144
145 FMalloc* UsedMalloc;
146
147 int IsPartOf(void* InPtr);
148 int FindAddressSpaceStompDataBlockIndex(void* InPtr, int Index);
149
150
151 /** If it is set to true, instead of focusing on overruns the allocator will focus on underruns. */
152 const bool bUseUnderrunMode;
153
154
155public:
156 // FMalloc interface.
157 FMallocStomp2(FMalloc* InMalloc, const bool InUseUnderrunMode = false);
158
159 /**
160 * Allocates a block of a given number of bytes of memory with the required alignment.
161 * In the process it allocates as many pages as necessary plus one that will be protected
162 * making it unaccessible and causing an exception. The actual allocation will be pushed
163 * to the end of the last valid unprotected page. To deal with underrun errors a sentinel
164 * is added right before the allocation in page which is checked on free.
165 *
166 * @param Size Size in bytes of the memory block to allocate.
167 * @param Alignment Alignment in bytes of the memory block to allocate.
168 * @return A pointer to the beginning of the memory block.
169 */
170 virtual void* Malloc(SIZE_T Size, uint32 Alignment) override;
171
172 virtual void* TryMalloc(SIZE_T Size, uint32 Alignment) override;
173
174 /**
175 * Changes the size of the memory block pointed to by OldPtr.
176 * The function may move the memory block to a new location.
177 *
178 * @param OldPtr Pointer to a memory block previously allocated with Malloc.
179 * @param NewSize New size in bytes for the memory block.
180 * @param Alignment Alignment in bytes for the reallocation.
181 * @return A pointer to the reallocated memory block, which may be either the same as ptr or a new location.
182 */
183 virtual void* Realloc(void* InPtr, SIZE_T NewSize, uint32 Alignment) override;
184
185 virtual void* TryRealloc(void* InPtr, SIZE_T NewSize, uint32 Alignment) override;
186
187 /**
188 * Frees a memory allocation and verifies the sentinel in the process.
189 *
190 * @param InPtr Pointer of the data to free.
191 */
192 virtual void Free(void* InPtr) override;
193
194 /**
195 * If possible determine the size of the memory allocated at the given address.
196 * This will included all the pages that were allocated so it will be far more
197 * than what's set on the FAllocationData.
198 *
199 * @param Original - Pointer to memory we are checking the size of
200 * @param SizeOut - If possible, this value is set to the size of the passed in pointer
201 * @return true if succeeded
202 */
203 virtual bool GetAllocationSize(void *Original, SIZE_T &SizeOut) override;
204
205 /**
206 * Dumps details about all allocations to an output device
207 *
208 * @param Ar [in] Output device
209 */
210 virtual void DumpAllocatorStats( FOutputDevice& Ar ) override
211 {
212 // No meaningful stats to dump.
213 }
214
215 /**
216 * Validates the allocator's heap
217 */
218 virtual bool ValidateHeap() override
219 {
220 // Nothing to do here since validation happens as data is accessed
221 // through page protection, and on each free checking the sentinel.
222 return true;
223 }
224
225 virtual const TCHAR* GetDescriptiveName() override
226 {
227 return TEXT( "Stomp2" );
228 }
229
230 virtual bool IsInternallyThreadSafe() const override
231 {
232 // Stomp allocator 2 is thread-safe
233 return true;
234 }
235
236 static FMalloc* OverrideIfEnabled(FMalloc* InUsedAlloc);
237
238 void Init();
239
240
241 static uint32 DisableTlsSlot;
242
243
244 /** We don't handle allocations smaller than this size. */
245 static SIZE_T MinSize;
246 /** We don't handle allocations larger than this size. */
247 static SIZE_T MaxSize;
248};
249
250
251/**
252 * Implements a scoped disabling of the stomp allocator 2
253 *
254 * We use this when we need to make a memory allocation that we don't want/need to track
255 *
256 */
257class FScopeDisableMallocStomp2
258{
259public:
260
261 /**
262 * Constructor that increments the TLS disables stomallocator2 counter
263 */
264 FScopeDisableMallocStomp2()
265 {
266 uint64 DisableCounter = (uint64)FPlatformTLS::GetTlsValue(FMallocStomp2::DisableTlsSlot);
267 ++DisableCounter;
268 FPlatformTLS::SetTlsValue(FMallocStomp2::DisableTlsSlot, (void*)DisableCounter);
269 }
270
271 /** Destructor that performs a decrement on the TLS disable stomallocator2 counter */
272 ~FScopeDisableMallocStomp2()
273 {
274 uint64 DisableCounter = (uint64)FPlatformTLS::GetTlsValue(FMallocStomp2::DisableTlsSlot);
275 --DisableCounter;
276 FPlatformTLS::SetTlsValue(FMallocStomp2::DisableTlsSlot, (void*)DisableCounter);
277 }
278};
279
280extern FMallocStomp2* GMallocStomp2;
281extern bool GMallocStomp2Enabled;
282
283#endif //WITH_MALLOC_STOMP2
#define WITH_MALLOC_STOMP2
Definition MallocStomp2.h:9