1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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 copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include "atexit.h"
30 
31 #include <errno.h>
32 #include <pthread.h>
33 #include <stdint.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/mman.h>
37 #include <sys/param.h>
38 #include <sys/prctl.h>
39 
40 #include <async_safe/CHECK.h>
41 #include <async_safe/log.h>
42 
43 #include "platform/bionic/page.h"
44 
45 extern "C" void __libc_stdio_cleanup();
46 extern "C" void __unregister_atfork(void* dso);
47 
48 namespace {
49 
50 struct AtexitEntry {
51   void (*fn)(void*);  // the __cxa_atexit callback
52   void* arg;          // argument for `fn` callback
53   void* dso;          // shared module handle
54 };
55 
56 class AtexitArray {
57  public:
size() const58   size_t size() const { return size_; }
total_appends() const59   uint64_t total_appends() const { return total_appends_; }
operator [](size_t idx) const60   const AtexitEntry& operator[](size_t idx) const { return array_[idx]; }
61 
62   bool append_entry(const AtexitEntry& entry);
63   AtexitEntry extract_entry(size_t idx);
64   void recompact();
65 
66  private:
67   AtexitEntry* array_;
68   size_t size_;
69   size_t extracted_count_;
70   size_t capacity_;
71 
72   // An entry can be appended by a __cxa_finalize callback. Track the number of appends so we
73   // restart concurrent __cxa_finalize passes.
74   uint64_t total_appends_;
75 
round_up_to_page_bytes(size_t capacity)76   static size_t round_up_to_page_bytes(size_t capacity) {
77     return PAGE_END(capacity * sizeof(AtexitEntry));
78   }
79 
next_capacity(size_t capacity)80   static size_t next_capacity(size_t capacity) {
81     // Double the capacity each time.
82     size_t result = round_up_to_page_bytes(MAX(1, capacity * 2)) / sizeof(AtexitEntry);
83     CHECK(result > capacity);
84     return result;
85   }
86 
87   // Recompact the array if it will save at least one page of memory at the end.
needs_recompaction()88   bool needs_recompaction() {
89     return round_up_to_page_bytes(size_ - extracted_count_) < round_up_to_page_bytes(size_);
90   }
91 
92   void set_writable(bool writable);
93   bool expand_capacity();
94 };
95 
96 }  // anonymous namespace
97 
append_entry(const AtexitEntry & entry)98 bool AtexitArray::append_entry(const AtexitEntry& entry) {
99   bool result = false;
100 
101   set_writable(true);
102   if (size_ < capacity_ || expand_capacity()) {
103     array_[size_++] = entry;
104     ++total_appends_;
105     result = true;
106   }
107   set_writable(false);
108 
109   return result;
110 }
111 
112 // Extract an entry and return it.
extract_entry(size_t idx)113 AtexitEntry AtexitArray::extract_entry(size_t idx) {
114   AtexitEntry result = array_[idx];
115 
116   set_writable(true);
117   array_[idx] = {};
118   ++extracted_count_;
119   set_writable(false);
120 
121   return result;
122 }
123 
recompact()124 void AtexitArray::recompact() {
125   if (!needs_recompaction()) return;
126 
127   set_writable(true);
128 
129   // Optimization: quickly skip over the initial non-null entries.
130   size_t src = 0, dst = 0;
131   while (src < size_ && array_[src].fn != nullptr) {
132     ++src;
133     ++dst;
134   }
135 
136   // Shift the non-null entries forward, and zero out the removed entries at the end of the array.
137   for (; src < size_; ++src) {
138     const AtexitEntry entry = array_[src];
139     array_[src] = {};
140     if (entry.fn != nullptr) {
141       array_[dst++] = entry;
142     }
143   }
144 
145   // If the table uses fewer pages, clean the pages at the end.
146   size_t old_bytes = round_up_to_page_bytes(size_);
147   size_t new_bytes = round_up_to_page_bytes(dst);
148   if (new_bytes < old_bytes) {
149     madvise(reinterpret_cast<char*>(array_) + new_bytes, old_bytes - new_bytes, MADV_DONTNEED);
150   }
151 
152   size_ = dst;
153   extracted_count_ = 0;
154 
155   set_writable(false);
156 }
157 
158 // Use mprotect to make the array writable or read-only. Returns true on success. Making the array
159 // read-only could protect against either unintentional or malicious corruption of the array.
set_writable(bool writable)160 void AtexitArray::set_writable(bool writable) {
161   if (array_ == nullptr) return;
162   const int prot = PROT_READ | (writable ? PROT_WRITE : 0);
163   if (mprotect(array_, round_up_to_page_bytes(capacity_), prot) != 0) {
164     async_safe_fatal("mprotect failed on atexit array: %s", strerror(errno));
165   }
166 }
167 
expand_capacity()168 bool AtexitArray::expand_capacity() {
169   const size_t new_capacity = next_capacity(capacity_);
170   const size_t new_capacity_bytes = round_up_to_page_bytes(new_capacity);
171 
172   void* new_pages;
173   if (array_ == nullptr) {
174     new_pages = mmap(nullptr, new_capacity_bytes, PROT_READ | PROT_WRITE,
175                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
176   } else {
177     new_pages =
178         mremap(array_, round_up_to_page_bytes(capacity_), new_capacity_bytes, MREMAP_MAYMOVE);
179   }
180   if (new_pages == MAP_FAILED) {
181     async_safe_format_log(ANDROID_LOG_WARN, "libc",
182                           "__cxa_atexit: mmap/mremap failed to allocate %zu bytes: %s",
183                           new_capacity_bytes, strerror(errno));
184     return false;
185   }
186 
187   prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, new_pages, new_capacity_bytes, "atexit handlers");
188   array_ = static_cast<AtexitEntry*>(new_pages);
189   capacity_ = new_capacity;
190   return true;
191 }
192 
193 static AtexitArray g_array;
194 static pthread_mutex_t g_atexit_lock = PTHREAD_MUTEX_INITIALIZER;
195 
atexit_lock()196 static inline void atexit_lock() {
197   pthread_mutex_lock(&g_atexit_lock);
198 }
199 
atexit_unlock()200 static inline void atexit_unlock() {
201   pthread_mutex_unlock(&g_atexit_lock);
202 }
203 
204 // Register a function to be called either when a library is unloaded (dso != nullptr), or when the
205 // program exits (dso == nullptr). The `dso` argument is typically the address of a hidden
206 // __dso_handle variable. This function is also used as the backend for the atexit function.
207 //
208 // See https://itanium-cxx-abi.github.io/cxx-abi/abi.html#dso-dtor.
209 //
__cxa_atexit(void (* func)(void *),void * arg,void * dso)210 int __cxa_atexit(void (*func)(void*), void* arg, void* dso) {
211   int result = -1;
212 
213   if (func != nullptr) {
214     atexit_lock();
215     if (g_array.append_entry({.fn = func, .arg = arg, .dso = dso})) {
216       result = 0;
217     }
218     atexit_unlock();
219   }
220 
221   return result;
222 }
223 
__cxa_finalize(void * dso)224 void __cxa_finalize(void* dso) {
225   atexit_lock();
226 
227   static uint32_t call_depth = 0;
228   ++call_depth;
229 
230 restart:
231   const uint64_t total_appends = g_array.total_appends();
232 
233   for (ssize_t i = g_array.size() - 1; i >= 0; --i) {
234     if (g_array[i].fn == nullptr || (dso != nullptr && g_array[i].dso != dso)) continue;
235 
236     // Clear the entry in the array because its DSO handle will become invalid, and to avoid calling
237     // an entry again if __cxa_finalize is called recursively.
238     const AtexitEntry entry = g_array.extract_entry(i);
239 
240     atexit_unlock();
241     entry.fn(entry.arg);
242     atexit_lock();
243 
244     if (g_array.total_appends() != total_appends) goto restart;
245   }
246 
247   // Avoid recompaction on recursive calls because it's unnecessary and would require earlier,
248   // concurrent __cxa_finalize calls to restart. Skip recompaction on program exit too
249   // (dso == nullptr), because the memory will be reclaimed soon anyway.
250   --call_depth;
251   if (call_depth == 0 && dso != nullptr) {
252     g_array.recompact();
253   }
254 
255   atexit_unlock();
256 
257   if (dso != nullptr) {
258     __unregister_atfork(dso);
259   } else {
260     // If called via exit(), flush output of all open files.
261     __libc_stdio_cleanup();
262   }
263 }
264