• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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