Index: lib/sanitizer_common/sanitizer_allocator_primary32.h =================================================================== --- lib/sanitizer_common/sanitizer_allocator_primary32.h +++ lib/sanitizer_common/sanitizer_allocator_primary32.h @@ -268,7 +268,8 @@ struct SizeClassInfo { SpinMutex mutex; IntrusiveList free_list; - char padding[kCacheLineSize - sizeof(uptr) - + u32 rand_state; + char padding[kCacheLineSize - 2 * sizeof(uptr) - sizeof(IntrusiveList)]; }; COMPILER_CHECK(sizeof(SizeClassInfo) == kCacheLineSize); @@ -301,29 +302,62 @@ return &size_class_info_array[class_id]; } + bool PopulateBatches(AllocatorCache *c, SizeClassInfo *sci, uptr class_id, + TransferBatch **current_batch, uptr max_count, + uptr *pointers, uptr count) { + TransferBatch *b = *current_batch; + for (uptr i = 0; i < count; i++) { + if (!b) { + b = c->CreateBatch(class_id, this, (TransferBatch*)pointers[i]); + if (UNLIKELY(!b)) + return false; + b->Clear(); + } + b->Add((void*)pointers[i]); + if (b->Count() == max_count) { + sci->free_list.push_back(b); + b = nullptr; + } + } + *current_batch = b; + return true; + } + bool PopulateFreeList(AllocatorStats *stat, AllocatorCache *c, SizeClassInfo *sci, uptr class_id) { uptr size = ClassIdToSize(class_id); uptr reg = AllocateRegion(stat, class_id); if (UNLIKELY(!reg)) return false; + if (kRandomShuffleChunks) + if (UNLIKELY(sci->rand_state == 0)) + // The random state is initialized from ASLR (PIE) and time. + sci->rand_state = reinterpret_cast(this) ^ NanoTime(); uptr n_chunks = kRegionSize / (size + kMetadataSize); uptr max_count = TransferBatch::MaxCached(class_id); CHECK_GT(max_count, 0); TransferBatch *b = nullptr; + const uptr kShuffleArraySize = 48; + uptr shuffle_array[kShuffleArraySize]; + uptr j = 0; for (uptr i = reg; i < reg + n_chunks * size; i += size) { - if (!b) { - b = c->CreateBatch(class_id, this, (TransferBatch*)i); - if (UNLIKELY(!b)) + shuffle_array[j++] = i; + if (j == kShuffleArraySize) { + if (kRandomShuffleChunks) + RandomShuffle(shuffle_array, j, &sci->rand_state); + if (UNLIKELY(!PopulateBatches(c, sci, class_id, &b, max_count, + shuffle_array, j))) return false; - b->Clear(); - } - b->Add((void*)i); - if (b->Count() == max_count) { - sci->free_list.push_back(b); - b = nullptr; + j = 0; } } + if (j) { + if (kRandomShuffleChunks) + RandomShuffle(shuffle_array, j, &sci->rand_state); + if (UNLIKELY(!PopulateBatches(c, sci, class_id, &b, max_count, + shuffle_array, j))) + return false; + } if (b) { CHECK_GT(b->Count(), 0); sci->free_list.push_back(b); Index: lib/sanitizer_common/sanitizer_allocator_primary64.h =================================================================== --- lib/sanitizer_common/sanitizer_allocator_primary64.h +++ lib/sanitizer_common/sanitizer_allocator_primary64.h @@ -597,18 +597,6 @@ }; COMPILER_CHECK(sizeof(RegionInfo) >= kCacheLineSize); - u32 Rand(u32 *state) { // ANSI C linear congruential PRNG. - return (*state = *state * 1103515245 + 12345) >> 16; - } - - u32 RandN(u32 *state, u32 n) { return Rand(state) % n; } // [0, n) - - void RandomShuffle(u32 *a, u32 n, u32 *rand_state) { - if (n <= 1) return; - for (u32 i = n - 1; i > 0; i--) - Swap(a[i], a[RandN(rand_state, i + 1)]); - } - RegionInfo *GetRegionInfo(uptr class_id) const { CHECK_LT(class_id, kNumClasses); RegionInfo *regions = @@ -681,8 +669,10 @@ // Map more space for chunks, if necessary. if (new_space_end > region->mapped_user) { - if (!kUsingConstantSpaceBeg && region->mapped_user == 0) - region->rand_state = static_cast(region_beg >> 12); // From ASLR. + if (!kUsingConstantSpaceBeg && kRandomShuffleChunks) + if (UNLIKELY(region->mapped_user == 0)) + // The random state is initialized from ASLR. + region->rand_state = static_cast(region_beg >> 12); // Do the mmap for the user memory. uptr map_size = kUserMapSize; while (new_space_end > region->mapped_user + map_size) Index: lib/sanitizer_common/sanitizer_common.h =================================================================== --- lib/sanitizer_common/sanitizer_common.h +++ lib/sanitizer_common/sanitizer_common.h @@ -918,6 +918,19 @@ // be used to seed a PRNG. Defaults to blocking like the underlying syscall. bool GetRandom(void *buffer, uptr length, bool blocking = true); +INLINE u32 Rand(u32 *state) { // ANSI C linear congruential PRNG. + return (*state = *state * 1103515245 + 12345) >> 16; +} + +INLINE u32 RandN(u32 *state, u32 n) { return Rand(state) % n; } // [0, n) + +template +INLINE void RandomShuffle(T *a, u32 n, u32 *rand_state) { + if (n <= 1) return; + for (u32 i = n - 1; i > 0; i--) + Swap(a[i], a[RandN(rand_state, i + 1)]); +} + } // namespace __sanitizer inline void *operator new(__sanitizer::operator_new_size_type size, Index: test/scudo/random_shuffle.cpp =================================================================== --- test/scudo/random_shuffle.cpp +++ test/scudo/random_shuffle.cpp @@ -7,8 +7,6 @@ // RUN: %run %t 10000 > %T/random_shuffle_tmp_dir/out2 // RUN: not diff %T/random_shuffle_tmp_dir/out? // RUN: rm -rf %T/random_shuffle_tmp_dir -// UNSUPPORTED: i386-linux,arm-linux,armhf-linux,aarch64-linux,mips-linux,mipsel-linux,mips64-linux,mips64el-linux -// UNSUPPORTED: android // Tests that the allocator shuffles the chunks before returning to the user.