1 // Copyright 2018 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef LIBBRILLO_BRILLO_SECURE_ALLOCATOR_H_ 6 #define LIBBRILLO_BRILLO_SECURE_ALLOCATOR_H_ 7 8 #include <errno.h> 9 #include <sys/mman.h> 10 #include <unistd.h> 11 12 #include <limits> 13 #include <memory> 14 15 #include <base/callback_helpers.h> 16 #include <base/logging.h> 17 #include <brillo/brillo_export.h> 18 19 namespace brillo { 20 // SecureAllocator is a stateless derivation of std::allocator that clears 21 // the contents of the object on deallocation. Additionally, to prevent the 22 // memory from being leaked, we use the following defensive mechanisms: 23 // 24 // 1. Use page-aligned memory so that it can be locked (therefore, use mmap() 25 // instead of malloc()). Note that mlock()s are not inherited over fork(), 26 // 27 // 2. Always allocate memory in multiples of pages: this adds a memory overhead 28 // of ~1 page for each object. Moreover, the extra memory is not available 29 // for the allocated object to expand into: the container expects that the 30 // memory allocated to it matches the size set in reserve(). 31 // TODO(sarthakkukreti): Figure out if it is possible to propagate the real 32 // capacity to the container without an intrusive change to the STL. 33 // [Example: allow __recommend() override in allocators for containers.] 34 // 35 // 3. Mark the memory segments as undumpable, unmergeable. 36 // 37 // 4. Use MADV_WIPEONFORK: 38 // this results in a new anonymous vma instead of copying over the contents 39 // of the secure object after a fork(). By default [MADV_DOFORK], the vma is 40 // marked as copy-on-write, and the first process which writes to the secure 41 // object after fork get a new copy. This may break the security guarantees 42 // setup above. Another alternative is to use MADV_DONTFORK which results in 43 // the memory mapping not getting copied over to child process at all: this 44 // may result in cases where if the child process gets segmentation faults 45 // on attempts to access virtual addresses in the secure object's address 46 // range, 47 // 48 // With MADV_WIPEONFORK, the child processes can access the secure object 49 // memory safely, but the contents of the secure object appear as zero to 50 // the child process. Note that threads share the virtual address space and 51 // secure objects would be transparent across threads. 52 // TODO(sarthakkukreti): Figure out patterns to pass secure data over fork(). 53 template <typename T> 54 class BRILLO_PRIVATE SecureAllocator : public std::allocator<T> { 55 public: 56 using typename std::allocator<T>::pointer; 57 using typename std::allocator<T>::size_type; 58 using typename std::allocator<T>::value_type; 59 60 // Constructors that wrap over std::allocator. 61 // Make sure that the allocator's static members are only allocated once. SecureAllocator()62 SecureAllocator() noexcept : std::allocator<T>() {} SecureAllocator(const SecureAllocator & other)63 SecureAllocator(const SecureAllocator& other) noexcept 64 : std::allocator<T>(other) {} 65 66 template <class U> SecureAllocator(const SecureAllocator<U> & other)67 SecureAllocator(const SecureAllocator<U>& other) noexcept 68 : std::allocator<T>(other) {} 69 70 template <typename U> struct rebind { 71 typedef SecureAllocator<U> other; 72 }; 73 74 // Return cached max_size. Deprecated in C++17, removed in C++20. max_size()75 size_type max_size() const { return max_size_; } 76 77 // Allocation: allocate ceil(size/pagesize) for holding the data. 78 pointer allocate(size_type n, pointer hint = nullptr) { 79 pointer buffer = nullptr; 80 // Check if n can be theoretically allocated. 81 CHECK_LT(n, max_size()); 82 83 // std::allocator is expected to throw a std::bad_alloc on failing to 84 // allocate the memory correctly. Instead of returning a nullptr, which 85 // confuses the standard template library, use CHECK(false) to crash on 86 // the failure path. 87 base::ScopedClosureRunner fail_on_allocation_error(base::Bind([]() { 88 PLOG(ERROR) << "Failed to allocate secure memory"; 89 CHECK(false); 90 })); 91 92 // Check if n = 0: there's nothing to allocate; 93 if (n == 0) 94 return nullptr; 95 96 // Calculate the page-aligned buffer size. 97 size_type buffer_size = CalculatePageAlignedBufferSize(n); 98 99 // Memory locking granularity is per-page: mmap ceil(size/page size) pages. 100 buffer = reinterpret_cast<pointer>( 101 mmap(nullptr, buffer_size, PROT_READ | PROT_WRITE, 102 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); 103 if (buffer == MAP_FAILED) 104 return nullptr; 105 106 // Lock buffer into physical memory. 107 if (mlock(buffer, buffer_size)) { 108 CHECK_NE(errno, ENOMEM) << "It is likely that SecureAllocator have " 109 "exceeded the RLIMIT_MEMLOCK limit"; 110 return nullptr; 111 } 112 113 // Mark memory as non dumpable in a core dump. 114 if (madvise(buffer, buffer_size, MADV_DONTDUMP)) 115 return nullptr; 116 117 // Mark memory as non mergeable with another page, even if the contents 118 // are the same. 119 if (madvise(buffer, buffer_size, MADV_UNMERGEABLE)) { 120 // MADV_UNMERGEABLE is only available if the kernel has been configured 121 // with CONFIG_KSM set. If the CONFIG_KSM flag has not been set, then 122 // pages are not mergeable so this madvise option is not necessary. 123 // 124 // In the case where CONFIG_KSM is not set, EINVAL is the error set. 125 // Since this error value is expected in some cases, we don't return a 126 // nullptr. 127 if (errno != EINVAL) 128 return nullptr; 129 } 130 131 // Make this mapping available to child processes but don't copy data from 132 // the secure object's pages during fork. With MADV_DONTFORK, the 133 // vma is not mapped in the child process which leads to segmentation 134 // faults if the child process tries to access this address. For example, 135 // if the parent process creates a SecureObject, forks() and the child 136 // process tries to call the destructor at the virtual address. 137 if (madvise(buffer, buffer_size, MADV_WIPEONFORK)) 138 return nullptr; 139 140 ignore_result(fail_on_allocation_error.Release()); 141 142 // Allocation was successful. 143 return buffer; 144 } 145 146 // Destroy object before deallocation. Deprecated in C++17, removed in C++20. 147 // After destroying the object, clear the contents of where the object was 148 // stored. 149 template <class U> destroy(U * p)150 void destroy(U* p) { 151 // Return if the pointer is invalid. 152 if (!p) 153 return; 154 std::allocator<U>::destroy(p); 155 clear_contents(p, sizeof(U)); 156 } 157 deallocate(pointer p,size_type n)158 virtual void deallocate(pointer p, size_type n) { 159 // Check if n can be theoretically deallocated. 160 CHECK_LT(n, max_size()); 161 162 // Check if n = 0 or p is a nullptr: there's nothing to deallocate; 163 if (n == 0 || !p) 164 return; 165 166 // Calculate the page-aligned buffer size. 167 size_type buffer_size = CalculatePageAlignedBufferSize(n); 168 169 clear_contents(p, buffer_size); 170 munlock(p, buffer_size); 171 munmap(p, buffer_size); 172 } 173 174 protected: 175 // Force memset to not be optimized out. 176 // Original source commit: 31b02653c2560f8331934e879263beda44c6cc76 177 // Repo: https://android.googlesource.com/platform/external/minijail 178 #if defined(__clang__) 179 #define __attribute_no_opt __attribute__((optnone)) 180 #else 181 #define __attribute_no_opt __attribute__((__optimize__(0))) 182 #endif 183 184 // Zero-out all bytes in the allocated buffer. clear_contents(pointer v,size_type n)185 virtual void __attribute_no_opt clear_contents(pointer v, size_type n) { 186 if (!v) 187 return; 188 memset(v, 0, n); 189 } 190 191 #undef __attribute_no_opt 192 193 private: 194 // Calculate the page-aligned buffer size. CalculatePageAlignedBufferSize(size_type n)195 size_t CalculatePageAlignedBufferSize(size_type n) { 196 size_type real_size = n * sizeof(value_type); 197 size_type page_aligned_remainder = real_size % page_size_; 198 size_type padding = 199 page_aligned_remainder != 0 ? page_size_ - page_aligned_remainder : 0; 200 return real_size + padding; 201 } 202 CalculatePageSize()203 static size_t CalculatePageSize() { 204 long ret = sysconf(_SC_PAGESIZE); // NOLINT [runtime/int] 205 206 // Initialize page size. 207 CHECK_GT(ret, 0L); 208 return ret; 209 } 210 211 // Since the allocator reuses page size and max size consistently, 212 // cache these values initially and reuse. GetMaxSizeForType(size_t page_size)213 static size_t GetMaxSizeForType(size_t page_size) { 214 // Initialize max size that can be theoretically allocated. 215 // Calculate the max size that is page-aligned. 216 size_t max_theoretical_size = std::numeric_limits<size_type>::max(); 217 size_t max_page_aligned_size = 218 max_theoretical_size - (max_theoretical_size % page_size); 219 220 return max_page_aligned_size / sizeof(value_type); 221 } 222 223 // Page size on system. 224 static const size_type page_size_; 225 // Max theoretical count for type on system. 226 static const size_type max_size_; 227 }; 228 229 // Inline definitions are only allowed for static const members with integral 230 // constexpr initializers, define static members of SecureAllocator types here. 231 template <typename T> 232 const typename SecureAllocator<T>::size_type SecureAllocator<T>::page_size_ = 233 SecureAllocator<T>::CalculatePageSize(); 234 235 template <typename T> 236 const typename SecureAllocator<T>::size_type SecureAllocator<T>::max_size_ = 237 SecureAllocator<T>::GetMaxSizeForType(SecureAllocator<T>::page_size_); 238 239 } // namespace brillo 240 241 #endif // LIBBRILLO_BRILLO_SECURE_ALLOCATOR_H_ 242