This is an archive of the discontinued LLVM Phabricator instance.

[sanitizer_common] Fallback to secondary allocator if primary allocator is exhausted
AbandonedPublic

Authored by leonardchan on Aug 22 2023, 2:06 PM.

Details

Summary

The 64bit primary allocator has a unique failure mode where it can exhaust the entire region for a given size class in the initial pre-allocated address range. This can happen if the region size happens to be relatively small and we allocate a bunch of objects that fit into the largest size class. It's still possible though for there to be some space outside of this initial address range. For this, we can rely on the secondary allocator to mmap to find some space.

Diff Detail

Event Timeline

leonardchan created this revision.Aug 22 2023, 2:06 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 22 2023, 2:06 PM
Herald added a subscriber: Enna1. · View Herald Transcript
leonardchan requested review of this revision.Aug 22 2023, 2:06 PM

We have unittests, check TestCombinedAllocator. Can you please extend some of them?

compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
68

maybe it's more efficient to increase size and go larger class sizes?

76

I afraid this is not enough.
Sanitizers has some assumptions on how it will be allocated.
E.g. in asan

bool from_primary = PrimaryAllocator::CanAllocate(needed_size, alignment);

So every sanitizers needs to be investigated on implication from this change.

MaskRay added a comment.EditedAug 22 2023, 4:28 PM

This can happen if the region size happens to be relatively small and we allocate a bunch of objects that fit into the largest size class.

Can you elaborate? RISC-V?

leonardchan abandoned this revision.Aug 23 2023, 3:48 PM

This can happen if the region size happens to be relatively small and we allocate a bunch of objects that fit into the largest size class.

Can you elaborate? RISC-V?

When getting chunks from the freearray via PopulateFreeArray, the primary allocator will attempt to remap more space in a sizeclass's region. Before that, it calls IsRegionExhausted to see if there's any space left in the region to make the allocation. The check is essentially requested_size < kRegionSize - kFreeArraySize. If the region has no more user-allocatable space, it'll fail with The process has exhausted %zuMB for size class %zu. The max space per region (kRegionSize) is determined by the overall space provided for the primary allocator divided by the number of sizeclasses. So if, if we happen to allocate a lot of objects for a given sizeclass, we'll fail in this way. The frequency of this failure mode can be reduced by increasing kRegionSize (or alternatively decreasing kFreeArraySize).

For a 39bit VMA for RISCV, we don't have a lot of overall space for the primary allocator and the default size class map has a bunch of size classes, so the region size tended to be small. I think I found a size class map though that seems to work for us and reduce the number of sizeclasses (rounded) to 32. Abandoning this revision for now (unless I run into this again).

compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
68

Ok, I found a good enough SizeClassMap config that helps prevent this for now.