d: Fix accesses of immutable arrays using constant index still bounds checked
[official-gcc.git] / libsanitizer / sanitizer_common / sanitizer_allocator_combined.h
blobb76d36dcf5a4e430700f81bd3b80c747740c1c4a
1 //===-- sanitizer_allocator_combined.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 // Part of the Sanitizer Allocator.
11 //===----------------------------------------------------------------------===//
12 #ifndef SANITIZER_ALLOCATOR_H
13 #error This file must be included inside sanitizer_allocator.h
14 #endif
16 // This class implements a complete memory allocator by using two
17 // internal allocators:
18 // PrimaryAllocator is efficient, but may not allocate some sizes (alignments).
19 // When allocating 2^x bytes it should return 2^x aligned chunk.
20 // PrimaryAllocator is used via a local AllocatorCache.
21 // SecondaryAllocator can allocate anything, but is not efficient.
22 template <class PrimaryAllocator,
23 class LargeMmapAllocatorPtrArray = DefaultLargeMmapAllocatorPtrArray>
24 class CombinedAllocator {
25 public:
26 using AllocatorCache = typename PrimaryAllocator::AllocatorCache;
27 using SecondaryAllocator =
28 LargeMmapAllocator<typename PrimaryAllocator::MapUnmapCallback,
29 LargeMmapAllocatorPtrArray,
30 typename PrimaryAllocator::AddressSpaceView>;
32 void InitLinkerInitialized(s32 release_to_os_interval_ms) {
33 stats_.InitLinkerInitialized();
34 primary_.Init(release_to_os_interval_ms);
35 secondary_.InitLinkerInitialized();
38 void Init(s32 release_to_os_interval_ms, uptr heap_start = 0) {
39 stats_.Init();
40 primary_.Init(release_to_os_interval_ms, heap_start);
41 secondary_.Init();
44 void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) {
45 // Returning 0 on malloc(0) may break a lot of code.
46 if (size == 0)
47 size = 1;
48 if (size + alignment < size) {
49 Report("WARNING: %s: CombinedAllocator allocation overflow: "
50 "0x%zx bytes with 0x%zx alignment requested\n",
51 SanitizerToolName, size, alignment);
52 return nullptr;
54 uptr original_size = size;
55 // If alignment requirements are to be fulfilled by the frontend allocator
56 // rather than by the primary or secondary, passing an alignment lower than
57 // or equal to 8 will prevent any further rounding up, as well as the later
58 // alignment check.
59 if (alignment > 8)
60 size = RoundUpTo(size, alignment);
61 // The primary allocator should return a 2^x aligned allocation when
62 // requested 2^x bytes, hence using the rounded up 'size' when being
63 // serviced by the primary (this is no longer true when the primary is
64 // using a non-fixed base address). The secondary takes care of the
65 // alignment without such requirement, and allocating 'size' would use
66 // extraneous memory, so we employ 'original_size'.
67 void *res;
68 if (primary_.CanAllocate(size, alignment))
69 res = cache->Allocate(&primary_, primary_.ClassID(size));
70 else
71 res = secondary_.Allocate(&stats_, original_size, alignment);
72 if (alignment > 8)
73 CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0);
74 return res;
77 s32 ReleaseToOSIntervalMs() const {
78 return primary_.ReleaseToOSIntervalMs();
81 void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) {
82 primary_.SetReleaseToOSIntervalMs(release_to_os_interval_ms);
85 void ForceReleaseToOS() {
86 primary_.ForceReleaseToOS();
89 void Deallocate(AllocatorCache *cache, void *p) {
90 if (!p) return;
91 if (primary_.PointerIsMine(p))
92 cache->Deallocate(&primary_, primary_.GetSizeClass(p), p);
93 else
94 secondary_.Deallocate(&stats_, p);
97 void *Reallocate(AllocatorCache *cache, void *p, uptr new_size,
98 uptr alignment) {
99 if (!p)
100 return Allocate(cache, new_size, alignment);
101 if (!new_size) {
102 Deallocate(cache, p);
103 return nullptr;
105 CHECK(PointerIsMine(p));
106 uptr old_size = GetActuallyAllocatedSize(p);
107 uptr memcpy_size = Min(new_size, old_size);
108 void *new_p = Allocate(cache, new_size, alignment);
109 if (new_p)
110 internal_memcpy(new_p, p, memcpy_size);
111 Deallocate(cache, p);
112 return new_p;
115 bool PointerIsMine(const void *p) const {
116 if (primary_.PointerIsMine(p))
117 return true;
118 return secondary_.PointerIsMine(p);
121 bool FromPrimary(const void *p) const { return primary_.PointerIsMine(p); }
123 void *GetMetaData(const void *p) {
124 if (primary_.PointerIsMine(p))
125 return primary_.GetMetaData(p);
126 return secondary_.GetMetaData(p);
129 void *GetBlockBegin(const void *p) {
130 if (primary_.PointerIsMine(p))
131 return primary_.GetBlockBegin(p);
132 return secondary_.GetBlockBegin(p);
135 // This function does the same as GetBlockBegin, but is much faster.
136 // Must be called with the allocator locked.
137 void *GetBlockBeginFastLocked(const void *p) {
138 if (primary_.PointerIsMine(p))
139 return primary_.GetBlockBegin(p);
140 return secondary_.GetBlockBeginFastLocked(p);
143 uptr GetActuallyAllocatedSize(void *p) {
144 if (primary_.PointerIsMine(p))
145 return primary_.GetActuallyAllocatedSize(p);
146 return secondary_.GetActuallyAllocatedSize(p);
149 uptr TotalMemoryUsed() {
150 return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed();
153 void TestOnlyUnmap() { primary_.TestOnlyUnmap(); }
155 void InitCache(AllocatorCache *cache) {
156 cache->Init(&stats_);
159 void DestroyCache(AllocatorCache *cache) {
160 cache->Destroy(&primary_, &stats_);
163 void SwallowCache(AllocatorCache *cache) {
164 cache->Drain(&primary_);
167 void GetStats(AllocatorStatCounters s) const {
168 stats_.Get(s);
171 void PrintStats() {
172 primary_.PrintStats();
173 secondary_.PrintStats();
176 // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
177 // introspection API.
178 void ForceLock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
179 primary_.ForceLock();
180 secondary_.ForceLock();
183 void ForceUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
184 secondary_.ForceUnlock();
185 primary_.ForceUnlock();
188 // Iterate over all existing chunks.
189 // The allocator must be locked when calling this function.
190 void ForEachChunk(ForEachChunkCallback callback, void *arg) {
191 primary_.ForEachChunk(callback, arg);
192 secondary_.ForEachChunk(callback, arg);
195 private:
196 PrimaryAllocator primary_;
197 SecondaryAllocator secondary_;
198 AllocatorGlobalStats stats_;