1 /*
2  * Copyright (C) 2008 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 <errno.h>
30 #include <pthread.h>
31 #include <stdlib.h>
32 
33 #include "platform/bionic/macros.h"
34 
35 struct atfork_t {
36   atfork_t* next;
37   atfork_t* prev;
38 
39   void (*prepare)(void);
40   void (*child)(void);
41   void (*parent)(void);
42 
43   void* dso_handle;
44 };
45 
46 class atfork_list_t {
47  public:
atfork_list_t()48   constexpr atfork_list_t() : first_(nullptr), last_(nullptr) {}
49 
50   template<typename F>
walk_forward(F f)51   void walk_forward(F f) {
52     for (atfork_t* it = first_; it != nullptr; it = it->next) {
53       f(it);
54     }
55   }
56 
57   template<typename F>
walk_backwards(F f)58   void walk_backwards(F f) {
59     for (atfork_t* it = last_; it != nullptr; it = it->prev) {
60       f(it);
61     }
62   }
63 
push_back(atfork_t * entry)64   void push_back(atfork_t* entry) {
65     entry->next = nullptr;
66     entry->prev = last_;
67     if (entry->prev != nullptr) {
68       entry->prev->next = entry;
69     }
70     if (first_ == nullptr) {
71       first_ = entry;
72     }
73     last_ = entry;
74   }
75 
76   template<typename F>
remove_if(F predicate)77   void remove_if(F predicate) {
78     atfork_t* it = first_;
79     while (it != nullptr) {
80       if (predicate(it)) {
81         atfork_t* entry = it;
82         it = it->next;
83         remove(entry);
84       } else {
85         it = it->next;
86       }
87     }
88   }
89 
90  private:
remove(atfork_t * entry)91   void remove(atfork_t* entry) {
92     if (entry->prev != nullptr) {
93       entry->prev->next = entry->next;
94     } else {
95       first_ = entry->next;
96     }
97 
98     if (entry->next != nullptr) {
99       entry->next->prev = entry->prev;
100     } else {
101       last_ = entry->prev;
102     }
103 
104     free(entry);
105   }
106 
107   atfork_t* first_;
108   atfork_t* last_;
109 
110   BIONIC_DISALLOW_COPY_AND_ASSIGN(atfork_list_t);
111 };
112 
113 static pthread_mutex_t g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
114 static atfork_list_t g_atfork_list;
115 
__bionic_atfork_run_prepare()116 void __bionic_atfork_run_prepare() {
117   // We lock the atfork list here, unlock it in the parent, and reset it in the child.
118   // This ensures that nobody can modify the handler array between the calls
119   // to the prepare and parent/child handlers.
120   pthread_mutex_lock(&g_atfork_list_mutex);
121 
122   // Call pthread_atfork() prepare handlers. POSIX states that the prepare
123   // handlers should be called in the reverse order of the parent/child
124   // handlers, so we iterate backwards.
125   g_atfork_list.walk_backwards([](atfork_t* it) {
126     if (it->prepare != nullptr) {
127       it->prepare();
128     }
129   });
130 }
131 
__bionic_atfork_run_child()132 void __bionic_atfork_run_child() {
133   g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
134 
135   pthread_mutex_lock(&g_atfork_list_mutex);
136   g_atfork_list.walk_forward([](atfork_t* it) {
137     if (it->child != nullptr) {
138       it->child();
139     }
140   });
141   pthread_mutex_unlock(&g_atfork_list_mutex);
142 }
143 
__bionic_atfork_run_parent()144 void __bionic_atfork_run_parent() {
145   g_atfork_list.walk_forward([](atfork_t* it) {
146     if (it->parent != nullptr) {
147       it->parent();
148     }
149   });
150 
151   pthread_mutex_unlock(&g_atfork_list_mutex);
152 }
153 
154 // __register_atfork is the name used by glibc
__register_atfork(void (* prepare)(void),void (* parent)(void),void (* child)(void),void * dso)155 extern "C" int __register_atfork(void (*prepare)(void), void (*parent)(void),
156                                  void(*child)(void), void* dso) {
157   atfork_t* entry = reinterpret_cast<atfork_t*>(malloc(sizeof(atfork_t)));
158   if (entry == nullptr) {
159     return ENOMEM;
160   }
161 
162   entry->prepare = prepare;
163   entry->parent = parent;
164   entry->child = child;
165   entry->dso_handle = dso;
166 
167   pthread_mutex_lock(&g_atfork_list_mutex);
168 
169   g_atfork_list.push_back(entry);
170 
171   pthread_mutex_unlock(&g_atfork_list_mutex);
172 
173   return 0;
174 }
175 
__unregister_atfork(void * dso)176 extern "C" __LIBC_HIDDEN__ void __unregister_atfork(void* dso) {
177   pthread_mutex_lock(&g_atfork_list_mutex);
178   g_atfork_list.remove_if([&](const atfork_t* entry) {
179     return entry->dso_handle == dso;
180   });
181   pthread_mutex_unlock(&g_atfork_list_mutex);
182 }
183