//===-- scudo_allocator_secondary.h -----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// Scudo Secondary Allocator. /// This services allocation that are too large to be serviced by the Primary /// Allocator. It is directly backed by the memory mapping functions of the /// operating system. /// //===----------------------------------------------------------------------===// #ifndef SCUDO_ALLOCATOR_SECONDARY_H_ #define SCUDO_ALLOCATOR_SECONDARY_H_ #ifndef SCUDO_ALLOCATOR_H_ # error "This file must be included inside scudo_allocator.h." #endif class ScudoLargeMmapAllocator { public: void Init() { PageSizeCached = GetPageSizeCached(); } void *Allocate(AllocatorStats *Stats, uptr Size, uptr Alignment) { const uptr UserSize = Size - AlignedChunkHeaderSize; // The Scudo frontend prevents us from allocating more than // MaxAllowedMallocSize, so integer overflow checks would be superfluous. uptr MapSize = Size + AlignedReservedAddressRangeSize; if (Alignment > MinAlignment) MapSize += Alignment; const uptr PageSize = PageSizeCached; MapSize = RoundUpTo(MapSize, PageSize); // Account for 2 guard pages, one before and one after the chunk. MapSize += 2 * PageSize; ReservedAddressRange AddressRange; uptr MapBeg = AddressRange.Init(MapSize); if (MapBeg == ~static_cast(0)) return ReturnNullOrDieOnFailure::OnOOM(); // A page-aligned pointer is assumed after that, so check it now. CHECK(IsAligned(MapBeg, PageSize)); uptr MapEnd = MapBeg + MapSize; // The beginning of the user area for that allocation comes after the // initial guard page, and both headers. This is the pointer that has to // abide by alignment requirements. uptr UserBeg = MapBeg + PageSize + HeadersSize; uptr UserEnd = UserBeg + UserSize; // In the rare event of larger alignments, we will attempt to fit the mmap // area better and unmap extraneous memory. This will also ensure that the // offset and unused bytes field of the header stay small. if (Alignment > MinAlignment) { if (!IsAligned(UserBeg, Alignment)) { UserBeg = RoundUpTo(UserBeg, Alignment); CHECK_GE(UserBeg, MapBeg); uptr NewMapBeg = RoundDownTo(UserBeg - HeadersSize, PageSize) - PageSize; CHECK_GE(NewMapBeg, MapBeg); if (NewMapBeg != MapBeg) { AddressRange.Unmap(MapBeg, NewMapBeg - MapBeg); MapBeg = NewMapBeg; } UserEnd = UserBeg + UserSize; } uptr NewMapEnd = RoundUpTo(UserEnd, PageSize) + PageSize; if (NewMapEnd != MapEnd) { AddressRange.Unmap(NewMapEnd, MapEnd - NewMapEnd); MapEnd = NewMapEnd; } MapSize = MapEnd - MapBeg; } CHECK_LE(UserEnd, MapEnd - PageSize); // Actually mmap the memory, preserving the guard pages on either side CHECK_EQ(MapBeg + PageSize, AddressRange.Map(MapBeg + PageSize, MapSize - 2 * PageSize)); const uptr Ptr = UserBeg - AlignedChunkHeaderSize; ReservedAddressRange *StoredRange = getReservedAddressRange(Ptr); *StoredRange = AddressRange; // The primary adds the whole class size to the stats when allocating a // chunk, so we will do something similar here. But we will not account for // the guard pages. { SpinMutexLock l(&StatsMutex); Stats->Add(AllocatorStatAllocated, MapSize - 2 * PageSize); Stats->Add(AllocatorStatMapped, MapSize - 2 * PageSize); } return reinterpret_cast(Ptr); } void Deallocate(AllocatorStats *Stats, void *Ptr) { // Since we're unmapping the entirety of where the ReservedAddressRange // actually is, copy onto the stack. const uptr PageSize = PageSizeCached; ReservedAddressRange AddressRange = *getReservedAddressRange(Ptr); { SpinMutexLock l(&StatsMutex); Stats->Sub(AllocatorStatAllocated, AddressRange.size() - 2 * PageSize); Stats->Sub(AllocatorStatMapped, AddressRange.size() - 2 * PageSize); } AddressRange.Unmap(reinterpret_cast(AddressRange.base()), AddressRange.size()); } uptr GetActuallyAllocatedSize(void *Ptr) { ReservedAddressRange *StoredRange = getReservedAddressRange(Ptr); // Deduct PageSize as ReservedAddressRange size includes the trailing guard // page. uptr MapEnd = reinterpret_cast(StoredRange->base()) + StoredRange->size() - PageSizeCached; return MapEnd - reinterpret_cast(Ptr); } private: ReservedAddressRange *getReservedAddressRange(uptr Ptr) { return reinterpret_cast( Ptr - sizeof(ReservedAddressRange)); } ReservedAddressRange *getReservedAddressRange(const void *Ptr) { return getReservedAddressRange(reinterpret_cast(Ptr)); } static constexpr uptr AlignedReservedAddressRangeSize = (sizeof(ReservedAddressRange) + MinAlignment - 1) & ~(MinAlignment - 1); static constexpr uptr HeadersSize = AlignedReservedAddressRangeSize + AlignedChunkHeaderSize; uptr PageSizeCached; SpinMutex StatsMutex; }; #endif // SCUDO_ALLOCATOR_SECONDARY_H_