@@ -20,6 +20,10 @@ namespace mta::memory
20
20
constexpr std::uint32_t NULL_PAGE_BOUNDARY = 0x10000 ;
21
21
constexpr std::uint32_t MAX_ADDRESS_SPACE = 0xFFFFFFFF ;
22
22
constexpr std::uint32_t POINTER_SIZE = 4 ;
23
+ constexpr std::uint32_t POINTER_METADATA_OVERHEAD = POINTER_SIZE * 2 ;
24
+ constexpr std::uint32_t METADATA_MAGIC = 0x4D544100 ; // 'MTA\0'
25
+ constexpr std::uint32_t METADATA_MAGIC_MASK = 0xFFFFFFFE ;
26
+ constexpr std::uint32_t METADATA_FLAG_VIRTUALALLOC = 0x1 ;
23
27
24
28
constexpr bool is_valid_alignment (std::size_t alignment) noexcept
25
29
{
@@ -28,7 +32,7 @@ namespace mta::memory
28
32
29
33
void * SafeMallocAlignVirtual (std::size_t size, std::size_t alignment) noexcept ;
30
34
31
- // Aligned malloc - stores pointer at result-4
35
+ // Aligned malloc - stores pointer at result-4 and metadata at result-8
32
36
[[nodiscard]] void * SafeMallocAlign (std::size_t size, std::size_t alignment) noexcept
33
37
{
34
38
// Check alignment
@@ -56,18 +60,19 @@ namespace mta::memory
56
60
const std::uint32_t align_u32 = static_cast <std::uint32_t >(alignment);
57
61
58
62
// Prevent intermediate overflow
59
- if (size_u32 > UINT32_MAX - align_u32 )
63
+ if (align_u32 > UINT32_MAX - POINTER_METADATA_OVERHEAD )
60
64
{
61
65
errno = ENOMEM;
62
66
return nullptr ;
63
67
}
64
- // Now safe to add size_u32 + align_u32
65
- if (size_u32 + align_u32 > UINT32_MAX - POINTER_SIZE)
68
+ const std::uint32_t alignment_overhead = align_u32 + POINTER_METADATA_OVERHEAD;
69
+
70
+ if (size_u32 > UINT32_MAX - alignment_overhead)
66
71
{
67
72
errno = ENOMEM;
68
73
return nullptr ;
69
74
}
70
- const std::uint32_t total_size = size_u32 + align_u32 + POINTER_SIZE ;
75
+ const std::uint32_t total_size = size_u32 + alignment_overhead ;
71
76
72
77
void * raw_memory = malloc (total_size);
73
78
if (!raw_memory)
@@ -78,16 +83,16 @@ namespace mta::memory
78
83
79
84
const std::uint32_t raw_addr = reinterpret_cast <std::uint32_t >(raw_memory);
80
85
81
- if (raw_addr > MAX_ADDRESS_SPACE - POINTER_SIZE - align_u32 + 1 )
86
+ if (raw_addr > MAX_ADDRESS_SPACE - POINTER_METADATA_OVERHEAD - align_u32 + 1 )
82
87
{
83
88
free (raw_memory);
84
89
errno = ENOMEM;
85
90
return nullptr ;
86
91
}
87
92
88
- const std::uint32_t aligned_addr = (raw_addr + POINTER_SIZE + align_u32 - 1 ) & ~(align_u32 - 1 );
93
+ const std::uint32_t aligned_addr = (raw_addr + POINTER_METADATA_OVERHEAD + align_u32 - 1 ) & ~(align_u32 - 1 );
89
94
90
- if (aligned_addr < raw_addr + POINTER_SIZE || aligned_addr + size_u32 > raw_addr + total_size || aligned_addr + size_u32 < aligned_addr)
95
+ if (aligned_addr < raw_addr + POINTER_METADATA_OVERHEAD || aligned_addr + size_u32 > raw_addr + total_size || aligned_addr + size_u32 < aligned_addr)
91
96
{
92
97
free (raw_memory);
93
98
errno = EINVAL;
@@ -98,15 +103,20 @@ namespace mta::memory
98
103
99
104
// Validate store location
100
105
void ** store_location = reinterpret_cast <void **>(aligned_addr - POINTER_SIZE);
101
- if (reinterpret_cast <std::uint32_t >(store_location) < raw_addr ||
102
- reinterpret_cast <std::uint32_t >(store_location) > raw_addr + total_size - POINTER_SIZE)
106
+ std::uint32_t * metadata_location = reinterpret_cast <std::uint32_t *>(aligned_addr - POINTER_METADATA_OVERHEAD);
107
+ const std::uint32_t store_addr = reinterpret_cast <std::uint32_t >(store_location);
108
+ const std::uint32_t metadata_addr = reinterpret_cast <std::uint32_t >(metadata_location);
109
+
110
+ if (store_addr < raw_addr || store_addr > raw_addr + total_size - POINTER_SIZE ||
111
+ metadata_addr < raw_addr || metadata_addr > raw_addr + total_size - POINTER_SIZE)
103
112
{
104
113
free (raw_memory);
105
114
errno = EFAULT;
106
115
return nullptr ;
107
116
}
108
117
109
118
*store_location = raw_memory;
119
+ *metadata_location = METADATA_MAGIC;
110
120
111
121
return result;
112
122
}
@@ -127,18 +137,19 @@ namespace mta::memory
127
137
const std::uint32_t align_u32 = static_cast <std::uint32_t >(alignment);
128
138
129
139
// Prevent intermediate overflow
130
- if (size_u32 > UINT32_MAX - align_u32 )
140
+ if (align_u32 > UINT32_MAX - POINTER_METADATA_OVERHEAD )
131
141
{
132
142
errno = ENOMEM;
133
143
return nullptr ;
134
144
}
135
- // Now safe to add size_u32 + align_u32
136
- if (size_u32 + align_u32 > UINT32_MAX - POINTER_SIZE)
145
+ const std::uint32_t alignment_overhead = align_u32 + POINTER_METADATA_OVERHEAD;
146
+
147
+ if (size_u32 > UINT32_MAX - alignment_overhead)
137
148
{
138
149
errno = ENOMEM;
139
150
return nullptr ;
140
151
}
141
- const std::uint32_t total_size = size_u32 + align_u32 + POINTER_SIZE ;
152
+ const std::uint32_t total_size = size_u32 + alignment_overhead ;
142
153
143
154
void * raw_memory = malloc (total_size);
144
155
if (!raw_memory)
@@ -149,16 +160,16 @@ namespace mta::memory
149
160
150
161
const std::uint32_t raw_addr = reinterpret_cast <std::uint32_t >(raw_memory);
151
162
152
- if (raw_addr > MAX_ADDRESS_SPACE - POINTER_SIZE - align_u32 + 1 )
163
+ if (raw_addr > MAX_ADDRESS_SPACE - POINTER_METADATA_OVERHEAD - align_u32 + 1 )
153
164
{
154
165
free (raw_memory);
155
166
errno = ENOMEM;
156
167
return nullptr ;
157
168
}
158
169
159
- const std::uint32_t aligned_addr = (raw_addr + POINTER_SIZE + align_u32 - 1 ) & ~(align_u32 - 1 );
170
+ const std::uint32_t aligned_addr = (raw_addr + POINTER_METADATA_OVERHEAD + align_u32 - 1 ) & ~(align_u32 - 1 );
160
171
161
- if (aligned_addr < raw_addr + POINTER_SIZE || aligned_addr + size_u32 > raw_addr + total_size || aligned_addr + size_u32 < aligned_addr)
172
+ if (aligned_addr < raw_addr + POINTER_METADATA_OVERHEAD || aligned_addr + size_u32 > raw_addr + total_size || aligned_addr + size_u32 < aligned_addr)
162
173
{
163
174
free (raw_memory);
164
175
errno = EINVAL;
@@ -168,15 +179,20 @@ namespace mta::memory
168
179
void * result = reinterpret_cast <void *>(aligned_addr);
169
180
170
181
void ** store_location = reinterpret_cast <void **>(aligned_addr - POINTER_SIZE);
171
- if (reinterpret_cast <std::uint32_t >(store_location) < raw_addr ||
172
- reinterpret_cast <std::uint32_t >(store_location) > raw_addr + total_size - POINTER_SIZE)
182
+ std::uint32_t * metadata_location = reinterpret_cast <std::uint32_t *>(aligned_addr - POINTER_METADATA_OVERHEAD);
183
+ const std::uint32_t store_addr = reinterpret_cast <std::uint32_t >(store_location);
184
+ const std::uint32_t metadata_addr = reinterpret_cast <std::uint32_t >(metadata_location);
185
+
186
+ if (store_addr < raw_addr || store_addr > raw_addr + total_size - POINTER_SIZE ||
187
+ metadata_addr < raw_addr || metadata_addr > raw_addr + total_size - POINTER_SIZE)
173
188
{
174
189
free (raw_memory);
175
190
errno = EFAULT;
176
191
return nullptr ;
177
192
}
178
193
179
194
*store_location = raw_memory;
195
+ *metadata_location = METADATA_MAGIC;
180
196
181
197
return result;
182
198
}
@@ -206,23 +222,25 @@ namespace mta::memory
206
222
const std::uint32_t align_u32 = static_cast <std::uint32_t >(alignment);
207
223
const std::uint32_t padding = (align_u32 <= 64 ) ? 32 : VIRTUALALLOC_PADDING;
208
224
209
- if (align_u32 > UINT32_MAX - POINTER_SIZE )
225
+ if (align_u32 > UINT32_MAX - POINTER_METADATA_OVERHEAD )
210
226
{
211
227
errno = ENOMEM;
212
228
return nullptr ;
213
229
}
214
- if (align_u32 + POINTER_SIZE > UINT32_MAX - padding)
230
+ const std::uint32_t alignment_overhead = align_u32 + POINTER_METADATA_OVERHEAD;
231
+
232
+ if (alignment_overhead > UINT32_MAX - padding)
215
233
{
216
234
errno = ENOMEM;
217
235
return nullptr ;
218
236
}
219
- if (size_u32 > UINT32_MAX - align_u32 - POINTER_SIZE - padding)
237
+ if (size_u32 > UINT32_MAX - alignment_overhead - padding)
220
238
{
221
239
errno = ENOMEM;
222
240
return nullptr ;
223
241
}
224
242
225
- const DWORD total_size = size_u32 + align_u32 + POINTER_SIZE + padding;
243
+ const DWORD total_size = size_u32 + alignment_overhead + padding;
226
244
227
245
void * raw_ptr = VirtualAlloc (nullptr , static_cast <SIZE_T>(total_size), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
228
246
if (!raw_ptr)
@@ -233,17 +251,17 @@ namespace mta::memory
233
251
234
252
const std::uint32_t raw_addr = reinterpret_cast <std::uint32_t >(raw_ptr);
235
253
236
- if (raw_addr > MAX_ADDRESS_SPACE - POINTER_SIZE - align_u32 + 1 )
254
+ if (raw_addr > MAX_ADDRESS_SPACE - POINTER_METADATA_OVERHEAD - align_u32 + 1 )
237
255
{
238
256
BOOL vfree_result = VirtualFree (raw_ptr, 0 , MEM_RELEASE);
239
257
(void )vfree_result;
240
258
errno = ENOMEM;
241
259
return nullptr ;
242
260
}
243
261
244
- const std::uint32_t aligned_addr = (raw_addr + POINTER_SIZE + align_u32 - 1 ) & ~(align_u32 - 1 );
262
+ const std::uint32_t aligned_addr = (raw_addr + POINTER_METADATA_OVERHEAD + align_u32 - 1 ) & ~(align_u32 - 1 );
245
263
246
- if (aligned_addr < raw_addr + POINTER_SIZE || aligned_addr + size_u32 > raw_addr + total_size || aligned_addr + size_u32 < aligned_addr)
264
+ if (aligned_addr < raw_addr + POINTER_METADATA_OVERHEAD || aligned_addr + size_u32 > raw_addr + total_size || aligned_addr + size_u32 < aligned_addr)
247
265
{
248
266
BOOL vfree_result = VirtualFree (raw_ptr, 0 , MEM_RELEASE);
249
267
(void )vfree_result;
@@ -255,8 +273,12 @@ namespace mta::memory
255
273
256
274
// Validate store location
257
275
void ** store_location = reinterpret_cast <void **>(aligned_addr - POINTER_SIZE);
258
- if (reinterpret_cast <std::uint32_t >(store_location) < raw_addr ||
259
- reinterpret_cast <std::uint32_t >(store_location) > raw_addr + total_size - POINTER_SIZE)
276
+ std::uint32_t * metadata_location = reinterpret_cast <std::uint32_t *>(aligned_addr - POINTER_METADATA_OVERHEAD);
277
+ const std::uint32_t store_addr = reinterpret_cast <std::uint32_t >(store_location);
278
+ const std::uint32_t metadata_addr = reinterpret_cast <std::uint32_t >(metadata_location);
279
+
280
+ if (store_addr < raw_addr || store_addr > raw_addr + total_size - POINTER_SIZE ||
281
+ metadata_addr < raw_addr || metadata_addr > raw_addr + total_size - POINTER_SIZE)
260
282
{
261
283
BOOL vfree_result = VirtualFree (raw_ptr, 0 , MEM_RELEASE);
262
284
(void )vfree_result;
@@ -265,6 +287,7 @@ namespace mta::memory
265
287
}
266
288
267
289
*store_location = raw_ptr;
290
+ *metadata_location = METADATA_MAGIC | METADATA_FLAG_VIRTUALALLOC;
268
291
269
292
return result;
270
293
}
@@ -282,7 +305,13 @@ namespace mta::memory
282
305
return ;
283
306
}
284
307
308
+ if (ptr_addr < POINTER_METADATA_OVERHEAD)
309
+ {
310
+ return ;
311
+ }
312
+
285
313
void ** read_location = reinterpret_cast <void **>(ptr_addr - POINTER_SIZE);
314
+ std::uint32_t * metadata_location = reinterpret_cast <std::uint32_t *>(ptr_addr - POINTER_METADATA_OVERHEAD);
286
315
287
316
// Validate memory readable
288
317
MEMORY_BASIC_INFORMATION mbi_read;
@@ -293,7 +322,35 @@ namespace mta::memory
293
322
return ;
294
323
}
295
324
325
+ const std::uint32_t metadata_addr = reinterpret_cast <std::uint32_t >(metadata_location);
326
+ const std::uint32_t base_addr = reinterpret_cast <std::uint32_t >(mbi_read.BaseAddress );
327
+
328
+ if (mbi_read.RegionSize == 0 || mbi_read.RegionSize > static_cast <SIZE_T>(MAX_ADDRESS_SPACE))
329
+ {
330
+ return ;
331
+ }
332
+
333
+ const std::uint32_t region_size_u32 = static_cast <std::uint32_t >(mbi_read.RegionSize );
334
+
335
+ if (base_addr > MAX_ADDRESS_SPACE - region_size_u32)
336
+ {
337
+ return ;
338
+ }
339
+
340
+ const std::uint32_t region_end = base_addr + region_size_u32;
341
+
342
+ if (region_size_u32 < POINTER_SIZE || metadata_addr < base_addr || metadata_addr > region_end - POINTER_SIZE)
343
+ {
344
+ return ;
345
+ }
346
+
296
347
void * original_ptr = *read_location;
348
+ const std::uint32_t metadata = *metadata_location;
349
+
350
+ if ((metadata & METADATA_MAGIC_MASK) != METADATA_MAGIC)
351
+ {
352
+ return ;
353
+ }
297
354
298
355
if (!original_ptr)
299
356
{
@@ -308,12 +365,12 @@ namespace mta::memory
308
365
}
309
366
310
367
const std::uint32_t distance = ptr_addr - original_addr;
311
- if (distance > MAX_ALIGNMENT + POINTER_SIZE )
368
+ if (distance > MAX_ALIGNMENT + POINTER_METADATA_OVERHEAD )
312
369
{
313
370
return ; // Beyond maximum possible alignment
314
371
}
315
372
316
- if (ptr_addr < POINTER_SIZE || original_addr > ptr_addr - POINTER_SIZE)
373
+ if (original_addr > ptr_addr - POINTER_SIZE)
317
374
{
318
375
return ; // Violates our storage pattern
319
376
}
@@ -323,31 +380,13 @@ namespace mta::memory
323
380
return ;
324
381
}
325
382
326
- MEMORY_BASIC_INFORMATION mbi;
327
- SIZE_T mbi_result = VirtualQuery (original_ptr, &mbi, sizeof (mbi));
328
-
329
- if (mbi_result == sizeof (mbi))
383
+ if ((metadata & METADATA_FLAG_VIRTUALALLOC) != 0 )
330
384
{
331
- const std::uint32_t base_addr = reinterpret_cast <std::uint32_t >(mbi.AllocationBase );
332
-
333
- // Validate region size
334
- if (mbi.RegionSize > 0 && mbi.RegionSize <= static_cast <SIZE_T>(MAX_ADDRESS_SPACE) &&
335
- base_addr <= MAX_ADDRESS_SPACE - static_cast <std::uint32_t >(mbi.RegionSize ))
336
- {
337
- const std::uint32_t region_size_u32 = static_cast <std::uint32_t >(mbi.RegionSize );
338
- const std::uint32_t region_end = base_addr + region_size_u32;
339
-
340
- // Use VirtualFree if matches
341
- if (mbi.Type == MEM_PRIVATE && mbi.State == MEM_COMMIT && original_addr >= base_addr && original_addr < region_end)
342
- {
343
- BOOL vfree_result = VirtualFree (mbi.AllocationBase , 0 , MEM_RELEASE);
344
- (void )vfree_result;
345
- return ;
346
- }
347
- }
385
+ BOOL vfree_result = VirtualFree (original_ptr, 0 , MEM_RELEASE);
386
+ (void )vfree_result;
387
+ return ;
348
388
}
349
389
350
- // Use free for malloc
351
390
free (original_ptr);
352
391
}
353
392
} // namespace mta::memory
0 commit comments