xref: /aosp_15_r20/external/google-breakpad/src/common/memory_allocator.h (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1 // Copyright 2009 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 //     * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //     * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 //     * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #ifndef GOOGLE_BREAKPAD_COMMON_MEMORY_ALLOCATOR_H_
30 #define GOOGLE_BREAKPAD_COMMON_MEMORY_ALLOCATOR_H_
31 
32 #include <stdint.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <sys/mman.h>
36 
37 #include <memory>
38 #include <vector>
39 
40 #if defined(MEMORY_SANITIZER)
41 #include <sanitizer/msan_interface.h>
42 #endif
43 
44 #ifdef __APPLE__
45 #define sys_mmap mmap
46 #define sys_munmap munmap
47 #define MAP_ANONYMOUS MAP_ANON
48 #else
49 #include "third_party/lss/linux_syscall_support.h"
50 #endif
51 
52 namespace google_breakpad {
53 
54 // This is very simple allocator which fetches pages from the kernel directly.
55 // Thus, it can be used even when the heap may be corrupted.
56 //
57 // There is no free operation. The pages are only freed when the object is
58 // destroyed.
59 class PageAllocator {
60  public:
PageAllocator()61   PageAllocator()
62       : page_size_(getpagesize()),
63         last_(NULL),
64         current_page_(NULL),
65         page_offset_(0),
66         pages_allocated_(0) {
67   }
68 
~PageAllocator()69   ~PageAllocator() {
70     FreeAll();
71   }
72 
Alloc(size_t bytes)73   void* Alloc(size_t bytes) {
74     if (!bytes)
75       return NULL;
76 
77     if (current_page_ && page_size_ - page_offset_ >= bytes) {
78       uint8_t* const ret = current_page_ + page_offset_;
79       page_offset_ += bytes;
80       if (page_offset_ == page_size_) {
81         page_offset_ = 0;
82         current_page_ = NULL;
83       }
84 
85       return ret;
86     }
87 
88     const size_t pages =
89         (bytes + sizeof(PageHeader) + page_size_ - 1) / page_size_;
90     uint8_t* const ret = GetNPages(pages);
91     if (!ret)
92       return NULL;
93 
94     page_offset_ =
95         (page_size_ - (page_size_ * pages - (bytes + sizeof(PageHeader)))) %
96         page_size_;
97     current_page_ = page_offset_ ? ret + page_size_ * (pages - 1) : NULL;
98 
99     return ret + sizeof(PageHeader);
100   }
101 
102   // Checks whether the page allocator owns the passed-in pointer.
103   // This method exists for testing pursposes only.
OwnsPointer(const void * p)104   bool OwnsPointer(const void* p) {
105     for (PageHeader* header = last_; header; header = header->next) {
106       const char* current = reinterpret_cast<char*>(header);
107       if ((p >= current) && (p < current + header->num_pages * page_size_))
108         return true;
109     }
110 
111     return false;
112   }
113 
pages_allocated()114   unsigned long pages_allocated() { return pages_allocated_; }
115 
116  private:
GetNPages(size_t num_pages)117   uint8_t* GetNPages(size_t num_pages) {
118     void* a = sys_mmap(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE,
119                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
120     if (a == MAP_FAILED)
121       return NULL;
122 
123 #if defined(MEMORY_SANITIZER)
124     // We need to indicate to MSan that memory allocated through sys_mmap is
125     // initialized, since linux_syscall_support.h doesn't have MSan hooks.
126     __msan_unpoison(a, page_size_ * num_pages);
127 #endif
128 
129     struct PageHeader* header = reinterpret_cast<PageHeader*>(a);
130     header->next = last_;
131     header->num_pages = num_pages;
132     last_ = header;
133 
134     pages_allocated_ += num_pages;
135 
136     return reinterpret_cast<uint8_t*>(a);
137   }
138 
FreeAll()139   void FreeAll() {
140     PageHeader* next;
141 
142     for (PageHeader* cur = last_; cur; cur = next) {
143       next = cur->next;
144       sys_munmap(cur, cur->num_pages * page_size_);
145     }
146   }
147 
148   struct PageHeader {
149     PageHeader* next;  // pointer to the start of the next set of pages.
150     size_t num_pages;  // the number of pages in this set.
151   };
152 
153   const size_t page_size_;
154   PageHeader* last_;
155   uint8_t* current_page_;
156   size_t page_offset_;
157   unsigned long pages_allocated_;
158 };
159 
160 // Wrapper to use with STL containers
161 template <typename T>
162 struct PageStdAllocator {
163   using AllocatorTraits = std::allocator_traits<std::allocator<T>>;
164   using value_type = typename AllocatorTraits::value_type;
165   using pointer = typename AllocatorTraits::pointer;
166   using difference_type = typename AllocatorTraits::difference_type;
167   using size_type = typename AllocatorTraits::size_type;
168 
PageStdAllocatorPageStdAllocator169   explicit PageStdAllocator(PageAllocator& allocator) : allocator_(allocator),
170                                                         stackdata_(NULL),
171                                                         stackdata_size_(0)
172   {}
173 
PageStdAllocatorPageStdAllocator174   template <class Other> PageStdAllocator(const PageStdAllocator<Other>& other)
175       : allocator_(other.allocator_),
176         stackdata_(nullptr),
177         stackdata_size_(0)
178   {}
179 
PageStdAllocatorPageStdAllocator180   explicit PageStdAllocator(PageAllocator& allocator,
181                             pointer stackdata,
182                             size_type stackdata_size) : allocator_(allocator),
183       stackdata_(stackdata),
184       stackdata_size_(stackdata_size)
185   {}
186 
187   inline pointer allocate(size_type n, const void* = 0) {
188     const size_type size = sizeof(T) * n;
189     if (size <= stackdata_size_) {
190       return stackdata_;
191     }
192     return static_cast<pointer>(allocator_.Alloc(size));
193   }
194 
deallocatePageStdAllocator195   inline void deallocate(pointer, size_type) {
196     // The PageAllocator doesn't free.
197   }
198 
199   template <typename U> struct rebind {
200     typedef PageStdAllocator<U> other;
201   };
202 
203  private:
204   // Silly workaround for the gcc from Android's ndk (gcc 4.6), which will
205   // otherwise complain that `other.allocator_` is private in the constructor
206   // code.
207   template<typename Other> friend struct PageStdAllocator;
208 
209   PageAllocator& allocator_;
210   pointer stackdata_;
211   size_type stackdata_size_;
212 };
213 
214 // A wasteful vector is a std::vector, except that it allocates memory from a
215 // PageAllocator. It's wasteful because, when resizing, it always allocates a
216 // whole new array since the PageAllocator doesn't support realloc.
217 template<class T>
218 class wasteful_vector : public std::vector<T, PageStdAllocator<T> > {
219  public:
220   wasteful_vector(PageAllocator* allocator, unsigned size_hint = 16)
221       : std::vector<T, PageStdAllocator<T> >(PageStdAllocator<T>(*allocator)) {
222     std::vector<T, PageStdAllocator<T> >::reserve(size_hint);
223   }
224  protected:
wasteful_vector(PageStdAllocator<T> allocator)225   wasteful_vector(PageStdAllocator<T> allocator)
226       : std::vector<T, PageStdAllocator<T> >(allocator) {}
227 };
228 
229 // auto_wasteful_vector allocates space on the stack for N entries to avoid
230 // using the PageAllocator for small data, while still allowing for larger data.
231 template<class T, unsigned int N>
232 class auto_wasteful_vector : public wasteful_vector<T> {
233  T stackdata_[N];
234  public:
auto_wasteful_vector(PageAllocator * allocator)235   auto_wasteful_vector(PageAllocator* allocator)
236       : wasteful_vector<T>(
237             PageStdAllocator<T>(*allocator,
238                                 &stackdata_[0],
239                                 sizeof(stackdata_))) {
240     std::vector<T, PageStdAllocator<T> >::reserve(N);
241   }
242 };
243 
244 }  // namespace google_breakpad
245 
new(size_t nbytes,google_breakpad::PageAllocator & allocator)246 inline void* operator new(size_t nbytes,
247                           google_breakpad::PageAllocator& allocator) {
248   return allocator.Alloc(nbytes);
249 }
250 
251 #endif  // GOOGLE_BREAKPAD_COMMON_MEMORY_ALLOCATOR_H_
252