File: | compiler-rt/lib/scudo/scudo_allocator_secondary.h |
Warning: | line 121, column 9 Value stored to 'ReservedEnd' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===-- scudo_allocator_secondary.h -----------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | /// |
9 | /// Scudo Secondary Allocator. |
10 | /// This services allocation that are too large to be serviced by the Primary |
11 | /// Allocator. It is directly backed by the memory mapping functions of the |
12 | /// operating system. |
13 | /// |
14 | //===----------------------------------------------------------------------===// |
15 | |
16 | #ifndef SCUDO_ALLOCATOR_SECONDARY_H_ |
17 | #define SCUDO_ALLOCATOR_SECONDARY_H_ |
18 | |
19 | #ifndef SCUDO_ALLOCATOR_H_ |
20 | # error "This file must be included inside scudo_allocator.h." |
21 | #endif |
22 | |
23 | // Secondary backed allocations are standalone chunks that contain extra |
24 | // information stored in a LargeChunk::Header prior to the frontend's header. |
25 | // |
26 | // The secondary takes care of alignment requirements (so that it can release |
27 | // unnecessary pages in the rare event of larger alignments), and as such must |
28 | // know about the frontend's header size. |
29 | // |
30 | // Since Windows doesn't support partial releasing of a reserved memory region, |
31 | // we have to keep track of both the reserved and the committed memory. |
32 | // |
33 | // The resulting chunk resembles the following: |
34 | // |
35 | // +--------------------+ |
36 | // | Guard page(s) | |
37 | // +--------------------+ |
38 | // | Unused space* | |
39 | // +--------------------+ |
40 | // | LargeChunk::Header | |
41 | // +--------------------+ |
42 | // | {Unp,P}ackedHeader | |
43 | // +--------------------+ |
44 | // | Data (aligned) | |
45 | // +--------------------+ |
46 | // | Unused space** | |
47 | // +--------------------+ |
48 | // | Guard page(s) | |
49 | // +--------------------+ |
50 | |
51 | namespace LargeChunk { |
52 | struct Header { |
53 | ReservedAddressRange StoredRange; |
54 | uptr CommittedSize; |
55 | uptr Size; |
56 | }; |
57 | constexpr uptr getHeaderSize() { |
58 | return RoundUpTo(sizeof(Header), MinAlignment); |
59 | } |
60 | static Header *getHeader(uptr Ptr) { |
61 | return reinterpret_cast<Header *>(Ptr - getHeaderSize()); |
62 | } |
63 | static Header *getHeader(const void *Ptr) { |
64 | return getHeader(reinterpret_cast<uptr>(Ptr)); |
65 | } |
66 | } // namespace LargeChunk |
67 | |
68 | class LargeMmapAllocator { |
69 | public: |
70 | void Init() { |
71 | internal_memset(this, 0, sizeof(*this)); |
72 | } |
73 | |
74 | void *Allocate(AllocatorStats *Stats, uptr Size, uptr Alignment) { |
75 | const uptr UserSize = Size - Chunk::getHeaderSize(); |
76 | // The Scudo frontend prevents us from allocating more than |
77 | // MaxAllowedMallocSize, so integer overflow checks would be superfluous. |
78 | uptr ReservedSize = Size + LargeChunk::getHeaderSize(); |
79 | if (UNLIKELY(Alignment > MinAlignment)__builtin_expect(!!(Alignment > MinAlignment), 0)) |
80 | ReservedSize += Alignment; |
81 | const uptr PageSize = GetPageSizeCached(); |
82 | ReservedSize = RoundUpTo(ReservedSize, PageSize); |
83 | // Account for 2 guard pages, one before and one after the chunk. |
84 | ReservedSize += 2 * PageSize; |
85 | |
86 | ReservedAddressRange AddressRange; |
87 | uptr ReservedBeg = AddressRange.Init(ReservedSize, SecondaryAllocatorName); |
88 | if (UNLIKELY(ReservedBeg == ~static_cast<uptr>(0))__builtin_expect(!!(ReservedBeg == ~static_cast<uptr>(0 )), 0)) |
89 | return nullptr; |
90 | // A page-aligned pointer is assumed after that, so check it now. |
91 | DCHECK(IsAligned(ReservedBeg, PageSize)); |
92 | uptr ReservedEnd = ReservedBeg + ReservedSize; |
93 | // The beginning of the user area for that allocation comes after the |
94 | // initial guard page, and both headers. This is the pointer that has to |
95 | // abide by alignment requirements. |
96 | uptr CommittedBeg = ReservedBeg + PageSize; |
97 | uptr UserBeg = CommittedBeg + HeadersSize; |
98 | uptr UserEnd = UserBeg + UserSize; |
99 | uptr CommittedEnd = RoundUpTo(UserEnd, PageSize); |
100 | |
101 | // In the rare event of larger alignments, we will attempt to fit the mmap |
102 | // area better and unmap extraneous memory. This will also ensure that the |
103 | // offset and unused bytes field of the header stay small. |
104 | if (UNLIKELY(Alignment > MinAlignment)__builtin_expect(!!(Alignment > MinAlignment), 0)) { |
105 | if (!IsAligned(UserBeg, Alignment)) { |
106 | UserBeg = RoundUpTo(UserBeg, Alignment); |
107 | CommittedBeg = RoundDownTo(UserBeg - HeadersSize, PageSize); |
108 | const uptr NewReservedBeg = CommittedBeg - PageSize; |
109 | DCHECK_GE(NewReservedBeg, ReservedBeg); |
110 | if (!SANITIZER_WINDOWS0 && NewReservedBeg != ReservedBeg) { |
111 | AddressRange.Unmap(ReservedBeg, NewReservedBeg - ReservedBeg); |
112 | ReservedBeg = NewReservedBeg; |
113 | } |
114 | UserEnd = UserBeg + UserSize; |
115 | CommittedEnd = RoundUpTo(UserEnd, PageSize); |
116 | } |
117 | const uptr NewReservedEnd = CommittedEnd + PageSize; |
118 | DCHECK_LE(NewReservedEnd, ReservedEnd); |
119 | if (!SANITIZER_WINDOWS0 && NewReservedEnd != ReservedEnd) { |
120 | AddressRange.Unmap(NewReservedEnd, ReservedEnd - NewReservedEnd); |
121 | ReservedEnd = NewReservedEnd; |
Value stored to 'ReservedEnd' is never read | |
122 | } |
123 | } |
124 | |
125 | DCHECK_LE(UserEnd, CommittedEnd); |
126 | const uptr CommittedSize = CommittedEnd - CommittedBeg; |
127 | // Actually mmap the memory, preserving the guard pages on either sides. |
128 | CHECK_EQ(CommittedBeg, AddressRange.Map(CommittedBeg, CommittedSize))do { __sanitizer::u64 v1 = (__sanitizer::u64)((CommittedBeg)) ; __sanitizer::u64 v2 = (__sanitizer::u64)((AddressRange.Map( CommittedBeg, CommittedSize))); if (__builtin_expect(!!(!(v1 == v2)), 0)) __sanitizer::CheckFailed("/build/llvm-toolchain-snapshot-13~++20210726100616+dead50d4427c/compiler-rt/lib/scudo/scudo_allocator_secondary.h" , 128, "(" "(CommittedBeg)" ") " "==" " (" "(AddressRange.Map(CommittedBeg, CommittedSize))" ")", v1, v2); } while (false); |
129 | const uptr Ptr = UserBeg - Chunk::getHeaderSize(); |
130 | LargeChunk::Header *H = LargeChunk::getHeader(Ptr); |
131 | H->StoredRange = AddressRange; |
132 | H->Size = CommittedEnd - Ptr; |
133 | H->CommittedSize = CommittedSize; |
134 | |
135 | // The primary adds the whole class size to the stats when allocating a |
136 | // chunk, so we will do something similar here. But we will not account for |
137 | // the guard pages. |
138 | { |
139 | SpinMutexLock l(&StatsMutex); |
140 | Stats->Add(AllocatorStatAllocated, CommittedSize); |
141 | Stats->Add(AllocatorStatMapped, CommittedSize); |
142 | AllocatedBytes += CommittedSize; |
143 | if (LargestSize < CommittedSize) |
144 | LargestSize = CommittedSize; |
145 | NumberOfAllocs++; |
146 | } |
147 | |
148 | return reinterpret_cast<void *>(Ptr); |
149 | } |
150 | |
151 | void Deallocate(AllocatorStats *Stats, void *Ptr) { |
152 | LargeChunk::Header *H = LargeChunk::getHeader(Ptr); |
153 | // Since we're unmapping the entirety of where the ReservedAddressRange |
154 | // actually is, copy onto the stack. |
155 | ReservedAddressRange AddressRange = H->StoredRange; |
156 | const uptr Size = H->CommittedSize; |
157 | { |
158 | SpinMutexLock l(&StatsMutex); |
159 | Stats->Sub(AllocatorStatAllocated, Size); |
160 | Stats->Sub(AllocatorStatMapped, Size); |
161 | FreedBytes += Size; |
162 | NumberOfFrees++; |
163 | } |
164 | AddressRange.Unmap(reinterpret_cast<uptr>(AddressRange.base()), |
165 | AddressRange.size()); |
166 | } |
167 | |
168 | static uptr GetActuallyAllocatedSize(void *Ptr) { |
169 | return LargeChunk::getHeader(Ptr)->Size; |
170 | } |
171 | |
172 | void PrintStats() { |
173 | Printf("Stats: LargeMmapAllocator: allocated %zd times (%zd K), " |
174 | "freed %zd times (%zd K), remains %zd (%zd K) max %zd M\n", |
175 | NumberOfAllocs, AllocatedBytes >> 10, NumberOfFrees, |
176 | FreedBytes >> 10, NumberOfAllocs - NumberOfFrees, |
177 | (AllocatedBytes - FreedBytes) >> 10, LargestSize >> 20); |
178 | } |
179 | |
180 | private: |
181 | static constexpr uptr HeadersSize = |
182 | LargeChunk::getHeaderSize() + Chunk::getHeaderSize(); |
183 | |
184 | StaticSpinMutex StatsMutex; |
185 | u32 NumberOfAllocs; |
186 | u32 NumberOfFrees; |
187 | uptr AllocatedBytes; |
188 | uptr FreedBytes; |
189 | uptr LargestSize; |
190 | }; |
191 | |
192 | #endif // SCUDO_ALLOCATOR_SECONDARY_H_ |