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