@@ -239,31 +239,99 @@ class MallocMemorySummary : AllStatic {
239
239
240
240
/*
241
241
* Malloc tracking header.
242
- * To satisfy malloc alignment requirement, NMT uses 2 machine words for tracking purpose,
243
- * which ensures 8-bytes alignment on 32-bit systems and 16-bytes on 64-bit systems (Product build).
242
+ *
243
+ * If NMT is active (state >= minimal), we need to track allocations. A simple and cheap way to
244
+ * do this is by using malloc headers.
245
+ *
246
+ * The user allocation is preceded by a header and is immediately followed by a (possibly unaligned)
247
+ * footer canary:
248
+ *
249
+ * +--------------+------------- .... ------------------+-----+
250
+ * | header | user | can |
251
+ * | | allocation | ary |
252
+ * +--------------+------------- .... ------------------+-----+
253
+ * 16 bytes user size 2 byte
254
+ *
255
+ * Alignment:
256
+ *
257
+ * The start of the user allocation needs to adhere to malloc alignment. We assume 128 bits
258
+ * on both 64-bit/32-bit to be enough for that. So the malloc header is 16 bytes long on both
259
+ * 32-bit and 64-bit.
260
+ *
261
+ * Layout on 64-bit:
262
+ *
263
+ * 0 1 2 3 4 5 6 7
264
+ * +--------+--------+--------+--------+--------+--------+--------+--------+
265
+ * | 64-bit size | ...
266
+ * +--------+--------+--------+--------+--------+--------+--------+--------+
267
+ *
268
+ * 8 9 10 11 12 13 14 15 16 ++
269
+ * +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
270
+ * ... | bucket idx | pos idx | flags | unused | canary | ... User payload ....
271
+ * +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
272
+ *
273
+ * Layout on 32-bit:
274
+ *
275
+ * 0 1 2 3 4 5 6 7
276
+ * +--------+--------+--------+--------+--------+--------+--------+--------+
277
+ * | alt. canary | 32-bit size | ...
278
+ * +--------+--------+--------+--------+--------+--------+--------+--------+
279
+ *
280
+ * 8 9 10 11 12 13 14 15 16 ++
281
+ * +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
282
+ * ... | bucket idx | pos idx | flags | unused | canary | ... User payload ....
283
+ * +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
284
+ *
285
+ * Notes:
286
+ * - We have a canary in the two bytes directly preceding the user payload. That allows us to
287
+ * catch negative buffer overflows.
288
+ * - On 32-bit, due to the smaller size_t, we have some bits to spare. So we also have a second
289
+ * canary at the very start of the malloc header (generously sized 32 bits).
290
+ * - The footer canary consists of two bytes. Since the footer location may be unaligned to 16 bits,
291
+ * the bytes are stored individually.
244
292
*/
245
293
246
294
class MallocHeader {
247
- #ifdef _LP64
248
- size_t _size : 64 ;
249
- size_t _flags : 8 ;
250
- size_t _pos_idx : 16 ;
251
- size_t _bucket_idx: 40 ;
252
- #define MAX_MALLOCSITE_TABLE_SIZE right_n_bits (40 )
253
- #define MAX_BUCKET_LENGTH right_n_bits (16 )
254
- #else
255
- size_t _size : 32 ;
256
- size_t _flags : 8 ;
257
- size_t _pos_idx : 8 ;
258
- size_t _bucket_idx: 16 ;
259
- #define MAX_MALLOCSITE_TABLE_SIZE right_n_bits (16 )
260
- #define MAX_BUCKET_LENGTH right_n_bits (8 )
261
- #endif // _LP64
295
+
296
+ NOT_LP64 (uint32_t _alt_canary);
297
+ size_t _size;
298
+ uint16_t _bucket_idx;
299
+ uint16_t _pos_idx;
300
+ uint8_t _flags;
301
+ uint8_t _unused;
302
+ uint16_t _canary;
303
+
304
+ #define MAX_MALLOCSITE_TABLE_SIZE (USHRT_MAX - 1 )
305
+ #define MAX_BUCKET_LENGTH (USHRT_MAX - 1 )
306
+
307
+ static const uint16_t _header_canary_life_mark = 0xE99E ;
308
+ static const uint16_t _header_canary_dead_mark = 0xD99D ;
309
+ static const uint16_t _footer_canary_life_mark = 0xE88E ;
310
+ static const uint16_t _footer_canary_dead_mark = 0xD88D ;
311
+ NOT_LP64 (static const uint32_t _header_alt_canary_life_mark = 0xE99EE99E ;)
312
+ NOT_LP64 (static const uint32_t _header_alt_canary_dead_mark = 0xD88DD88D ;)
313
+
314
+ // We discount sizes larger than these
315
+ static const size_t max_reasonable_malloc_size = LP64_ONLY(256 * G) NOT_LP64(3500 * M);
316
+
317
+ // Check block integrity. If block is broken, print out a report
318
+ // to tty (optionally with hex dump surrounding the broken block),
319
+ // then trigger a fatal error.
320
+ void check_block_integrity () const ;
321
+ void print_block_on_error (outputStream* st, address bad_address) const ;
322
+ void mark_block_as_dead ();
323
+
324
+ static uint16_t build_footer (uint8_t b1, uint8_t b2) { return ((uint16_t )b1 << 8 ) | (uint16_t )b2; }
325
+
326
+ uint8_t * footer_address () const { return ((address)this ) + sizeof (MallocHeader) + _size; }
327
+ uint16_t get_footer () const { return build_footer (footer_address ()[0 ], footer_address ()[1 ]); }
328
+ void set_footer (uint16_t v) { footer_address ()[0 ] = v >> 8 ; footer_address ()[1 ] = (uint8_t )v; }
262
329
263
330
public:
331
+
264
332
MallocHeader (size_t size, MEMFLAGS flags, const NativeCallStack& stack, NMT_TrackingLevel level) {
265
- assert ( sizeof (MallocHeader) == sizeof ( void *) * 2 ,
266
- " Wrong header size" );
333
+
334
+ assert (size < max_reasonable_malloc_size, " Too large allocation size? " );
267
335
268
336
if (level == NMT_minimal) {
269
337
return ;
@@ -277,11 +345,18 @@ class MallocHeader {
277
345
if (record_malloc_site (stack, size, &bucket_idx, &pos_idx, flags)) {
278
346
assert (bucket_idx <= MAX_MALLOCSITE_TABLE_SIZE, " Overflow bucket index" );
279
347
assert (pos_idx <= MAX_BUCKET_LENGTH, " Overflow bucket position index" );
280
- _bucket_idx = bucket_idx;
281
- _pos_idx = pos_idx;
348
+ _bucket_idx = ( uint16_t ) bucket_idx;
349
+ _pos_idx = ( uint16_t ) pos_idx;
282
350
}
283
351
}
284
352
353
+ _unused = 0 ;
354
+ _canary = _header_canary_life_mark;
355
+ // On 32-bit we have some bits more, use them for a second canary
356
+ // guarding the start of the header.
357
+ NOT_LP64 (_alt_canary = _header_alt_canary_life_mark;)
358
+ set_footer (_footer_canary_life_mark); // set after initializing _size
359
+
285
360
MallocMemorySummary::record_malloc (size, flags);
286
361
MallocMemorySummary::record_new_malloc_header (sizeof (MallocHeader));
287
362
}
@@ -290,8 +365,8 @@ class MallocHeader {
290
365
inline MEMFLAGS flags () const { return (MEMFLAGS)_flags; }
291
366
bool get_stack (NativeCallStack& stack) const ;
292
367
293
- // Cleanup tracking information before the memory is released.
294
- void release () const ;
368
+ // Cleanup tracking information and mark block as dead before the memory is released.
369
+ void release ();
295
370
296
371
private:
297
372
inline void set_size (size_t size) {
@@ -301,6 +376,9 @@ class MallocHeader {
301
376
size_t * bucket_idx, size_t * pos_idx, MEMFLAGS flags) const ;
302
377
};
303
378
379
+ // This needs to be true on both 64-bit and 32-bit platforms
380
+ STATIC_ASSERT (sizeof (MallocHeader) == (sizeof (uint64_t ) * 2));
381
+
304
382
305
383
// Main class called from MemTracker to track malloc activities
306
384
class MallocTracker : AllStatic {
@@ -315,6 +393,11 @@ class MallocTracker : AllStatic {
315
393
return (level == NMT_off) ? 0 : sizeof (MallocHeader);
316
394
}
317
395
396
+ // malloc tracking footer size for specific tracking level
397
+ static inline size_t malloc_footer_size (NMT_TrackingLevel level) {
398
+ return (level == NMT_off) ? 0 : sizeof (uint16_t );
399
+ }
400
+
318
401
// Parameter name convention:
319
402
// memblock : the beginning address for user data
320
403
// malloc_base: the beginning address that includes malloc tracking header
@@ -349,11 +432,6 @@ class MallocTracker : AllStatic {
349
432
return header->flags ();
350
433
}
351
434
352
- // Get header size
353
- static inline size_t get_header_size (void * memblock) {
354
- return (memblock == NULL ) ? 0 : sizeof (MallocHeader);
355
- }
356
-
357
435
static inline void record_new_arena (MEMFLAGS flags) {
358
436
MallocMemorySummary::record_new_arena (flags);
359
437
}
0 commit comments