16
16
17
17
#include " scudo_allocator.h"
18
18
#include " scudo_utils.h"
19
- #include " scudo_allocator_secondary.h"
20
19
21
20
#include " sanitizer_common/sanitizer_allocator_interface.h"
22
21
#include " sanitizer_common/sanitizer_quarantine.h"
25
24
#include < pthread.h>
26
25
#include < smmintrin.h>
27
26
28
- #include < atomic>
29
27
#include < cstring>
30
28
31
29
namespace __scudo {
32
30
33
- const uptr MinAlignmentLog = 4 ; // 16 bytes for x64
34
- const uptr MaxAlignmentLog = 24 ;
35
-
36
31
struct AP {
37
32
static const uptr kSpaceBeg = ~0ULL ;
38
33
static const uptr kSpaceSize = 0x10000000000ULL ;
@@ -55,55 +50,18 @@ static thread_local Xorshift128Plus Prng;
55
50
// Global static cookie, initialized at start-up.
56
51
static u64 Cookie;
57
52
58
- enum ChunkState : u8 {
59
- ChunkAvailable = 0 ,
60
- ChunkAllocated = 1 ,
61
- ChunkQuarantine = 2
62
- };
63
-
64
- typedef unsigned __int128 PackedHeader;
65
- typedef std::atomic<PackedHeader> AtomicPackedHeader;
66
-
67
- // Our header requires 128-bit of storage on x64 (the only platform supported
68
- // as of now), which fits nicely with the alignment requirements.
69
- // Having the offset saves us from using functions such as GetBlockBegin, that
70
- // is fairly costly. Our first implementation used the MetaData as well, which
71
- // offers the advantage of being stored away from the chunk itself, but
72
- // accessing it was costly as well.
73
- // The header will be atomically loaded and stored using the 16-byte primitives
74
- // offered by the platform (likely requires cmpxchg16b support).
75
- struct UnpackedHeader {
76
- // 1st 8 bytes
77
- u16 Checksum : 16 ;
78
- u64 RequestedSize : 40 ; // Needed for reallocation purposes.
79
- u8 State : 2 ; // available, allocated, or quarantined
80
- u8 AllocType : 2 ; // malloc, new, new[], or memalign
81
- u8 Unused_0_ : 4 ;
82
- // 2nd 8 bytes
83
- u64 Offset : 20 ; // Offset from the beginning of the backend
84
- // allocation to the beginning of the chunk itself,
85
- // in multiples of MinAlignment. See comment about
86
- // its maximum value and test in init().
87
- u64 Unused_1_ : 28 ;
88
- u16 Salt : 16 ;
89
- };
90
-
91
- COMPILER_CHECK (sizeof (UnpackedHeader) == sizeof (PackedHeader));
92
-
93
- const uptr ChunkHeaderSize = sizeof (PackedHeader);
94
-
95
53
struct ScudoChunk : UnpackedHeader {
96
54
// We can't use the offset member of the chunk itself, as we would double
97
55
// fetch it without any warranty that it wouldn't have been tampered. To
98
56
// prevent this, we work with a local copy of the header.
99
- void *AllocBeg (UnpackedHeader *Header) {
57
+ void *getAllocBeg (UnpackedHeader *Header) {
100
58
return reinterpret_cast <void *>(
101
59
reinterpret_cast <uptr>(this ) - (Header->Offset << MinAlignmentLog));
102
60
}
103
61
104
62
// CRC32 checksum of the Chunk pointer and its ChunkHeader.
105
63
// It currently uses the Intel Nehalem SSE4.2 crc32 64-bit instruction.
106
- u16 Checksum (UnpackedHeader *Header) const {
64
+ u16 computeChecksum (UnpackedHeader *Header) const {
107
65
u64 HeaderHolder[2 ];
108
66
memcpy (HeaderHolder, Header, sizeof (HeaderHolder));
109
67
u64 Crc = _mm_crc32_u64 (Cookie, reinterpret_cast <uptr>(this ));
@@ -125,14 +83,14 @@ struct ScudoChunk : UnpackedHeader {
125
83
*NewUnpackedHeader = bit_cast<UnpackedHeader>(NewPackedHeader);
126
84
if ((NewUnpackedHeader->Unused_0_ != 0 ) ||
127
85
(NewUnpackedHeader->Unused_1_ != 0 ) ||
128
- (NewUnpackedHeader->Checksum != Checksum (NewUnpackedHeader))) {
86
+ (NewUnpackedHeader->Checksum != computeChecksum (NewUnpackedHeader))) {
129
87
dieWithMessage (" ERROR: corrupted chunk header at address %p\n " , this );
130
88
}
131
89
}
132
90
133
91
// Packs and stores the header, computing the checksum in the process.
134
92
void storeHeader (UnpackedHeader *NewUnpackedHeader) {
135
- NewUnpackedHeader->Checksum = Checksum (NewUnpackedHeader);
93
+ NewUnpackedHeader->Checksum = computeChecksum (NewUnpackedHeader);
136
94
PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader);
137
95
AtomicPackedHeader *AtomicHeader =
138
96
reinterpret_cast <AtomicPackedHeader *>(this );
@@ -144,7 +102,7 @@ struct ScudoChunk : UnpackedHeader {
144
102
// we are not being raced by a corruption occurring in another thread.
145
103
void compareExchangeHeader (UnpackedHeader *NewUnpackedHeader,
146
104
UnpackedHeader *OldUnpackedHeader) {
147
- NewUnpackedHeader->Checksum = Checksum (NewUnpackedHeader);
105
+ NewUnpackedHeader->Checksum = computeChecksum (NewUnpackedHeader);
148
106
PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader);
149
107
PackedHeader OldPackedHeader = bit_cast<PackedHeader>(*OldUnpackedHeader);
150
108
AtomicPackedHeader *AtomicHeader =
@@ -194,6 +152,8 @@ static void initInternal() {
194
152
Options.setFrom (getFlags (), common_flags ());
195
153
initAllocator (Options);
196
154
155
+ MaybeStartBackgroudThread ();
156
+
197
157
ScudoInitIsRunning = false ;
198
158
}
199
159
@@ -221,7 +181,7 @@ struct QuarantineCallback {
221
181
dieWithMessage (" ERROR: invalid chunk state when recycling address %p\n " ,
222
182
Chunk);
223
183
}
224
- void *Ptr = Chunk->AllocBeg (&Header);
184
+ void *Ptr = Chunk->getAllocBeg (&Header);
225
185
getAllocator ().Deallocate (Cache_, Ptr );
226
186
}
227
187
@@ -269,9 +229,8 @@ void AllocatorOptions::copyTo(Flags *f, CommonFlags *cf) const {
269
229
}
270
230
271
231
struct Allocator {
272
- static const uptr MaxAllowedMallocSize = 1ULL << 40 ;
273
- static const uptr MinAlignment = 1 << MinAlignmentLog;
274
- static const uptr MaxAlignment = 1 << MaxAlignmentLog; // 16 MB
232
+ static const uptr MaxAllowedMallocSize =
233
+ FIRST_32_SECOND_64 (2UL << 30 , 1ULL << 40 );
275
234
276
235
ScudoAllocator BackendAllocator;
277
236
ScudoQuarantine AllocatorQuarantine;
@@ -296,13 +255,18 @@ struct Allocator {
296
255
CHECK (testCPUFeature (SSE4_2)); // for crc32
297
256
298
257
// Verify that the header offset field can hold the maximum offset. In the
299
- // worst case scenario, the backend allocation is already aligned on
300
- // MaxAlignment, so in order to store the header and still be aligned, we
301
- // add an extra MaxAlignment. As a result, the offset from the beginning of
302
- // the backend allocation to the chunk will be MaxAlignment -
303
- // ChunkHeaderSize.
258
+ // case of the Secondary allocator, it takes care of alignment and the
259
+ // offset will always be 0. In the case of the Primary, the worst case
260
+ // scenario happens in the last size class, when the backend allocation
261
+ // would already be aligned on the requested alignment, which would happen
262
+ // to be the maximum alignment that would fit in that size class. As a
263
+ // result, the maximum offset will be at most the maximum alignment for the
264
+ // last size class minus the header size, in multiples of MinAlignment.
304
265
UnpackedHeader Header = {};
305
- uptr MaximumOffset = (MaxAlignment - ChunkHeaderSize) >> MinAlignmentLog;
266
+ uptr MaxPrimaryAlignment = 1 << MostSignificantSetBitIndex (
267
+ PrimaryAllocator::SizeClassMap::kMaxSize - MinAlignment);
268
+ uptr MaximumOffset = (MaxPrimaryAlignment - ChunkHeaderSize) >>
269
+ MinAlignmentLog;
306
270
Header.Offset = MaximumOffset;
307
271
if (Header.Offset != MaximumOffset) {
308
272
dieWithMessage (" ERROR: the maximum possible offset doesn't fit in the "
@@ -313,9 +277,9 @@ struct Allocator {
313
277
DeleteSizeMismatch = Options.DeleteSizeMismatch ;
314
278
ZeroContents = Options.ZeroContents ;
315
279
BackendAllocator.Init (Options.MayReturnNull );
316
- AllocatorQuarantine.Init (static_cast <uptr>(Options. QuarantineSizeMb ) << 20 ,
317
- static_cast <uptr>(
318
- Options.ThreadLocalQuarantineSizeKb ) << 10 );
280
+ AllocatorQuarantine.Init (
281
+ static_cast <uptr>(Options. QuarantineSizeMb ) << 20 ,
282
+ static_cast <uptr>( Options.ThreadLocalQuarantineSizeKb ) << 10 );
319
283
BackendAllocator.InitCache (&FallbackAllocatorCache);
320
284
Cookie = Prng.Next ();
321
285
}
@@ -325,7 +289,7 @@ struct Allocator {
325
289
if (UNLIKELY (!ThreadInited))
326
290
initThread ();
327
291
if (!IsPowerOfTwo (Alignment)) {
328
- dieWithMessage (" ERROR: malloc alignment is not a power of 2\n " );
292
+ dieWithMessage (" ERROR: alignment is not a power of 2\n " );
329
293
}
330
294
if (Alignment > MaxAlignment)
331
295
return BackendAllocator.ReturnNullOrDieOnBadRequest ();
@@ -336,20 +300,21 @@ struct Allocator {
336
300
if (Size >= MaxAllowedMallocSize)
337
301
return BackendAllocator.ReturnNullOrDieOnBadRequest ();
338
302
uptr RoundedSize = RoundUpTo (Size , MinAlignment);
339
- uptr ExtraBytes = ChunkHeaderSize;
303
+ uptr NeededSize = RoundedSize + ChunkHeaderSize;
340
304
if (Alignment > MinAlignment)
341
- ExtraBytes += Alignment;
342
- uptr NeededSize = RoundedSize + ExtraBytes;
305
+ NeededSize += Alignment;
343
306
if (NeededSize >= MaxAllowedMallocSize)
344
307
return BackendAllocator.ReturnNullOrDieOnBadRequest ();
308
+ bool FromPrimary = PrimaryAllocator::CanAllocate (NeededSize, MinAlignment);
345
309
346
310
void *Ptr ;
347
311
if (LIKELY (!ThreadTornDown)) {
348
- Ptr = BackendAllocator.Allocate (&Cache, NeededSize, MinAlignment);
312
+ Ptr = BackendAllocator.Allocate (&Cache, NeededSize,
313
+ FromPrimary ? MinAlignment : Alignment);
349
314
} else {
350
315
SpinMutexLock l (&FallbackMutex);
351
316
Ptr = BackendAllocator.Allocate (&FallbackAllocatorCache, NeededSize,
352
- MinAlignment);
317
+ FromPrimary ? MinAlignment : Alignment );
353
318
}
354
319
if (!Ptr )
355
320
return BackendAllocator.ReturnNullOrDieOnOOM ();
@@ -359,6 +324,11 @@ struct Allocator {
359
324
memset (Ptr , 0 , BackendAllocator.GetActuallyAllocatedSize (Ptr ));
360
325
361
326
uptr AllocBeg = reinterpret_cast <uptr>(Ptr );
327
+ // If the allocation was serviced by the secondary, the returned pointer
328
+ // accounts for ChunkHeaderSize to pass the alignment check of the combined
329
+ // allocator. Adjust it here.
330
+ if (!FromPrimary)
331
+ AllocBeg -= ChunkHeaderSize;
362
332
uptr ChunkBeg = AllocBeg + ChunkHeaderSize;
363
333
if (!IsAligned (ChunkBeg, Alignment))
364
334
ChunkBeg = RoundUpTo (ChunkBeg, Alignment);
@@ -450,7 +420,7 @@ struct Allocator {
450
420
" address %p\n " , Chunk);
451
421
}
452
422
uptr Size =
453
- BackendAllocator.GetActuallyAllocatedSize (Chunk->AllocBeg (Header));
423
+ BackendAllocator.GetActuallyAllocatedSize (Chunk->getAllocBeg (Header));
454
424
// UsableSize works as malloc_usable_size, which is also what (AFAIU)
455
425
// tcmalloc's MallocExtension::GetAllocatedSize aims at providing. This
456
426
// means we will return the size of the chunk from the user beginning to
@@ -543,7 +513,7 @@ void drainQuarantine() {
543
513
}
544
514
545
515
void *scudoMalloc (uptr Size , AllocType Type) {
546
- return Instance.allocate (Size , Allocator:: MinAlignment, Type);
516
+ return Instance.allocate (Size , MinAlignment, Type);
547
517
}
548
518
549
519
void scudoFree (void *Ptr , AllocType Type) {
@@ -556,7 +526,7 @@ void scudoSizedFree(void *Ptr, uptr Size, AllocType Type) {
556
526
557
527
void *scudoRealloc (void *Ptr , uptr Size ) {
558
528
if (!Ptr )
559
- return Instance.allocate (Size , Allocator:: MinAlignment, FromMalloc);
529
+ return Instance.allocate (Size , MinAlignment, FromMalloc);
560
530
if (Size == 0 ) {
561
531
Instance.deallocate (Ptr , 0 , FromMalloc);
562
532
return nullptr ;
0 commit comments