4#include "Stroika/Foundation/StroikaPreComp.h"
8#if qStroika_Foundation_Common_Platform_Linux
12#if qStroika_Foundation_Common_Platform_POSIX
16#include "Stroika/Foundation/Memory/Common.h"
24#if qStroika_Foundation_Debug_MallocGuard
26 constexpr array<byte, 16> kMallocGuardHeader_BASE_{
27 0xf3, 0xfa, 0x0b, 0x93, 0x48, 0x50, 0x46, 0xe6, 0x22, 0xf1, 0xfa, 0xc0, 0x9a, 0x0b, 0xeb, 0x23,
29 constexpr array<byte, 16> kMallocGuardFooter_BASE_{
30 0x07, 0x41, 0xa4, 0x2b, 0xba, 0x97, 0xcb, 0x38, 0x46, 0x1e, 0x3c, 0x42, 0x3c, 0x5f, 0x0c, 0x80,
33 using GuradBytes_ = array<byte, qStroika_Foundation_Debug_MallocGuard_GuardSize>;
34#if qStroika_Foundation_Debug_MallocGuard_GuardSize == 16
35 constexpr GuradBytes_ kMallocGuardHeader_{kMallocGuardHeader_BASE_};
36 constexpr GuradBytes_ kMallocGuardFooter_{kMallocGuardFooter_BASE_};
38 const GuradBytes_ kMallocGuardHeader_;
39 const GuradBytes_ kMallocGuardFooter_;
44 for (
size_t i = 0; i < NEltsOf (kMallocGuardHeader_); ++i) {
45 kMallocGuardHeader_[i] = kMallocGuardHeader_BASE_[fromI];
46 kMallocGuardFooter_[i] = kMallocGuardFooter_[fromI];
48 if (fromI >= NEltsOf (kMallocGuardHeader_BASE_)) {
57 constexpr byte kDeadMansLand_[] = {
58 0x1d, 0xb6, 0x20, 0x27, 0x43, 0x7a, 0x3d, 0x1a, 0x13, 0x65,
61 struct alignas (alignof (long double)) Header_ {
62 size_t fRequestedBlockSize;
65 struct alignas (alignof (long double)) Footer_ {
67 size_t fRequestedBlockSize;
70 void OhShit_ (
const char* why)
72 static bool sDone_{
false};
74 DISABLE_COMPILER_GCC_WARNING_START (
"GCC diagnostic ignored \"-Wunused-result\"")
76 const
char kMsg_[] = "Fatal Error detected in Stroika Malloc Guard\n";
77 ::write (2, kMsg_, NEltsOf (kMsg_));
80 ::write (2, why, ::strlen (why));
84 DISABLE_COMPILER_GCC_WARNING_END (
"GCC diagnostic ignored \"-Wunused-result\"")
89 void* ExposedPtrToBackendPtr_ (
void* p)
92 OhShit_ (
"unexpected nullptr in ExposedPtrToBackendPtr_");
94 return reinterpret_cast<Header_*
> (p) - 1;
96 void* BackendPtrToExposedPtr_ (
void* p)
99 OhShit_ (
"unexpected nullptr in BackendPtrToExposedPtr_");
101 return reinterpret_cast<Header_*
> (p) + 1;
103 size_t AdjustMallocSize_ (
size_t s)
106 return sizeof (Header_) + s +
sizeof (Footer_);
109 bool IsDeadMansLand_ (
const byte* s,
const byte* e)
114 void SetDeadMansLand_ (
byte* s,
byte* e)
116 const byte* pBadFillStart = begin (kDeadMansLand_);
117 const byte* pBadFillEnd = end (kDeadMansLand_);
118 const byte* badFillI = pBadFillStart;
119 for (
byte* oi = s; oi != e; ++oi) {
122 if (badFillI == pBadFillEnd) {
123 badFillI = pBadFillStart;
127 void SetDeadMansLand_ (
void* p)
129 const Header_* hp =
reinterpret_cast<const Header_*
> (p);
130 SetDeadMansLand_ (
reinterpret_cast<byte*
> (p),
reinterpret_cast<byte*
> (p) + AdjustMallocSize_ (hp->fRequestedBlockSize));
142 volatile void* sFreeList_[100];
143 volatile void** sFreeList_NextFreeI_ = &sFreeList_[0];
144 void Add2FreeList_ (
void* p)
146 *sFreeList_NextFreeI_ = p;
147 volatile void** next = sFreeList_NextFreeI_ + 1;
148 if (next >= end (sFreeList_)) {
149 next = begin (sFreeList_);
151 sFreeList_NextFreeI_ = next;
153 void* MyAtomicLoad_ (
volatile void** p)
157 return atomic_load_explicit (
reinterpret_cast<volatile atomic<void*>*
> (p), memory_order_acquire);
159 void MyAtomicStore_ (
volatile void** p,
void* value)
163 atomic_store_explicit (
reinterpret_cast<volatile atomic<void*>*
> (p), value, memory_order_release);
165 void ClearFromFreeList_ (
void* p)
168 for (
volatile void** i = begin (sFreeList_); i != end (sFreeList_); ++i) {
169 if (MyAtomicLoad_ (i) == p) {
170 MyAtomicStore_ (i,
nullptr);
174 bool IsInFreeList_ (
const void* p)
176 for (
volatile void** i = begin (sFreeList_); i != end (sFreeList_); ++i) {
177 if (MyAtomicLoad_ (i) == p) {
184 void Validate_ (
const Header_& header,
const Footer_& footer)
186 if (::memcmp (&header.fGuard, &kMallocGuardHeader_, sizeof (kMallocGuardHeader_)) != 0) {
187 OhShit_ (
"Invalid leading header guard");
189 if (::memcmp (&footer.fGuard, &kMallocGuardFooter_, sizeof (kMallocGuardFooter_)) != 0) {
190 OhShit_ (
"Invalid trailing footer guard");
192 if (header.fRequestedBlockSize != footer.fRequestedBlockSize) {
193 OhShit_ (
"Mismatch between header/trailer block sizes");
197 void ValidateBackendPtr_ (
const void* p)
199 if (IsInFreeList_ (p)) {
201 OhShit_ (
"Pointer already freed (recently)");
203 const Header_* hp =
reinterpret_cast<const Header_*
> (p);
204 const Footer_* fp =
reinterpret_cast<const Footer_*
> (
reinterpret_cast<const byte*
> (hp + 1) + hp->fRequestedBlockSize);
206 (void)::memcpy (&footer, fp,
sizeof (footer));
207 Validate_ (*hp, footer);
210 void PatchNewPointer_ (
void* p,
size_t requestedSize)
212 Header_* hp =
reinterpret_cast<Header_*
> (p);
213 (void)::memcpy (begin (hp->fGuard), begin (kMallocGuardHeader_), kMallocGuardHeader_.size ());
214 hp->fRequestedBlockSize = requestedSize;
215 Footer_* fp =
reinterpret_cast<Footer_*
> (
reinterpret_cast<byte*
> (hp + 1) + hp->fRequestedBlockSize);
216 (void)::memcpy (begin (fp->fGuard), begin (kMallocGuardFooter_), kMallocGuardFooter_.size ());
217 fp->fRequestedBlockSize = requestedSize;
222#if qStroika_Foundation_Debug_MallocGuard
224extern "C" void __libc_free (
void* __ptr);
225extern "C" void* __libc_malloc (
size_t __size);
226extern "C" void* __libc_realloc (
void* __ptr,
size_t __size);
227extern "C" void* __libc_calloc (
size_t __nmemb,
size_t __size);
228extern "C" void __libc_free (
void* __ptr);
230extern "C" void* calloc (
size_t __nmemb,
size_t __size)
232 size_t n = __nmemb * __size;
233 void* p = malloc (n);
234 (void)::memset (p, 0, n);
238extern "C" void cfree (
void* __ptr)
243extern "C" void free (
void* __ptr)
245 if (__ptr ==
nullptr) {
250 void* p = ExposedPtrToBackendPtr_ (__ptr);
251 ValidateBackendPtr_ (p);
252 SetDeadMansLand_ (p);
257extern "C" void* malloc (
size_t __size)
259 void* p = __libc_malloc (AdjustMallocSize_ (__size));
260 PatchNewPointer_ (p, __size);
261 ClearFromFreeList_ (p);
262 ValidateBackendPtr_ (p);
264 p = BackendPtrToExposedPtr_ (p);
269extern "C" void* realloc (
void* __ptr,
size_t __size)
271 if (__ptr ==
nullptr) {
274 return malloc (__size);
276 if (__ptr !=
nullptr and __size == 0) {
282 void* p = ExposedPtrToBackendPtr_ (__ptr);
283 ValidateBackendPtr_ (p);
284 size_t n = AdjustMallocSize_ (__size);
285 void* newP = __libc_realloc (p, n);
286 if (newP !=
nullptr) {
287 PatchNewPointer_ (newP, __size);
291 ClearFromFreeList_ (newP);
293 ValidateBackendPtr_ (newP);
294 newP = BackendPtrToExposedPtr_ (newP);
299extern "C" void* valloc (
size_t __size)
302 OhShit_ (
"valloc not supported in qStroika_Foundation_Debug_MallocGuard (valloc is OBSOLETE)");
306extern "C" void* pvalloc (
size_t __size)
309 OhShit_ (
"pvalloc not supported in qStroika_Foundation_Debug_MallocGuard (pvalloc is OBSOLETE)");
313extern "C" void* memalign (
size_t __alignment,
size_t __size)
316 OhShit_ (
"memalign not supported in qStroika_Foundation_Debug_MallocGuard (memalign is OBSOLETE)");
320extern "C" size_t malloc_usable_size (
void* ptr)
322 if (ptr ==
nullptr) {
325 void* p = ExposedPtrToBackendPtr_ (ptr);
326 ValidateBackendPtr_ (p);
327 const Header_* hp =
reinterpret_cast<const Header_*
> (p);
328 return hp->fRequestedBlockSize;
331extern "C" int posix_memalign (
void** memptr,
size_t alignment,
size_t size)
334 OhShit_ (
"posix_memalign () not supported in qStroika_Foundation_Debug_MallocGuard (it should be)");