s2n_fork_detection.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License").
  5. * You may not use this file except in compliance with the License.
  6. * A copy of the License is located at
  7. *
  8. * http://aws.amazon.com/apache2.0
  9. *
  10. * or in the "license" file accompanying this file. This file is distributed
  11. * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  12. * express or implied. See the License for the specific language governing
  13. * permissions and limitations under the License.
  14. */
  15. /* This captures Darwin specialities. This is the only APPLE flavor we care about.
  16. * Here we also capture varius required feature test macros.
  17. */
  18. #if defined(__APPLE__)
  19. typedef struct _opaque_pthread_once_t __darwin_pthread_once_t;
  20. typedef __darwin_pthread_once_t pthread_once_t;
  21. #define _DARWIN_C_SOURCE
  22. #elif defined(__FreeBSD__) || defined(__OpenBSD__)
  23. /* FreeBSD requires POSIX compatibility off for its syscalls (enables __BSD_VISIBLE)
  24. * Without the below line, <sys/mman.h> cannot be imported (it requires __BSD_VISIBLE) */
  25. #undef _POSIX_C_SOURCE
  26. #elif !defined(_GNU_SOURCE)
  27. /* Keep in sync with feature probe tests/features/madvise.c */
  28. #define _GNU_SOURCE
  29. #endif
  30. #include <sys/mman.h>
  31. /* Not always defined for Darwin */
  32. #if !defined(MAP_ANONYMOUS)
  33. #define MAP_ANONYMOUS MAP_ANON
  34. #endif
  35. #include <pthread.h>
  36. #include <stdlib.h>
  37. #include <sys/types.h>
  38. #include <unistd.h>
  39. #include "error/s2n_errno.h"
  40. #include "utils/s2n_fork_detection.h"
  41. #include "utils/s2n_safety.h"
  42. #if defined(S2N_MADVISE_SUPPORTED) && defined(MADV_WIPEONFORK)
  43. #if (MADV_WIPEONFORK != 18)
  44. #error "MADV_WIPEONFORK is not 18"
  45. #endif
  46. #else /* defined(S2N_MADVISE_SUPPORTED) && defined(MADV_WIPEONFORK) */
  47. #define MADV_WIPEONFORK 18
  48. #endif
  49. /* Sometimes (for example, on FreeBSD) MAP_INHERIT_ZERO is called INHERIT_ZERO */
  50. #if !defined(MAP_INHERIT_ZERO) && defined(INHERIT_ZERO)
  51. #define MAP_INHERIT_ZERO INHERIT_ZERO
  52. #endif
  53. /* These variables are used to disable all fork detection mechanisms or at the
  54. * individual level during testing.
  55. */
  56. static bool ignore_wipeonfork_or_inherit_zero_method_for_testing = false;
  57. static bool ignore_pthread_atfork_method_for_testing = false;
  58. static bool ignore_fork_detection_for_testing = false;
  59. #define S2N_FORK_EVENT 0
  60. #define S2N_NO_FORK_EVENT 1
  61. struct FGN_STATE {
  62. /* The current cached fork generation number for this process */
  63. uint64_t current_fork_generation_number;
  64. /* Semaphore controlling access to the shared sentinel and signaling whether
  65. * fork detection is enabled or not. We could use zero_on_fork_addr, but
  66. * avoid overloading by using an explicit variable.
  67. */
  68. bool is_fork_detection_enabled;
  69. /* Sentinel that signals a fork event has occurred */
  70. volatile char *zero_on_fork_addr;
  71. pthread_once_t fork_detection_once;
  72. pthread_rwlock_t fork_detection_rw_lock;
  73. };
  74. /* We only need a single statically initialised state. Note, the state is
  75. * inherited by child processes.
  76. */
  77. static struct FGN_STATE fgn_state = {
  78. .current_fork_generation_number = 0,
  79. .is_fork_detection_enabled = false,
  80. .zero_on_fork_addr = NULL,
  81. .fork_detection_once = PTHREAD_ONCE_INIT,
  82. .fork_detection_rw_lock = PTHREAD_RWLOCK_INITIALIZER,
  83. };
  84. /* Can currently never fail. See initialise_fork_detection_methods() for
  85. * motivation.
  86. */
  87. static inline S2N_RESULT s2n_initialise_wipeonfork_best_effort(void *addr, long page_size)
  88. {
  89. #if defined(S2N_MADVISE_SUPPORTED)
  90. /* Return value ignored on purpose */
  91. madvise(addr, (size_t) page_size, MADV_WIPEONFORK);
  92. #endif
  93. return S2N_RESULT_OK;
  94. }
  95. static inline S2N_RESULT s2n_initialise_inherit_zero(void *addr, long page_size)
  96. {
  97. #if defined(S2N_MINHERIT_SUPPORTED) && defined(MAP_INHERIT_ZERO)
  98. RESULT_ENSURE(minherit(addr, page_size, MAP_INHERIT_ZERO) == 0, S2N_ERR_FORK_DETECTION_INIT);
  99. #endif
  100. return S2N_RESULT_OK;
  101. }
  102. static void s2n_pthread_atfork_on_fork(void)
  103. {
  104. /* This zeroises the first byte of the memory page pointed to by
  105. * *zero_on_fork_addr. This is the same byte used as fork event detection
  106. * sentinel in s2n_get_fork_generation_number(). The same memory page, and in
  107. * turn, the byte, is also the memory zeroised by the MADV_WIPEONFORK fork
  108. * detection mechanism.
  109. *
  110. * Aquire locks to be on the safe side. We want to avoid the checks in
  111. * s2n_get_fork_generation_number() getting executed before setting the sentinel
  112. * flag. The write lock prevents any other thread from owning any other type
  113. * of lock.
  114. *
  115. * pthread_atfork_on_fork() cannot return errors. Hence, there is no way to
  116. * gracefully recover if [un]locking fails.
  117. */
  118. if (pthread_rwlock_wrlock(&fgn_state.fork_detection_rw_lock) != 0) {
  119. printf("pthread_rwlock_wrlock() failed. Aborting.\n");
  120. abort();
  121. }
  122. if (fgn_state.zero_on_fork_addr == NULL) {
  123. printf("fgn_state.zero_on_fork_addr is NULL. Aborting.\n");
  124. abort();
  125. }
  126. *fgn_state.zero_on_fork_addr = 0;
  127. if (pthread_rwlock_unlock(&fgn_state.fork_detection_rw_lock) != 0) {
  128. printf("pthread_rwlock_unlock() failed. Aborting.\n");
  129. abort();
  130. }
  131. }
  132. static S2N_RESULT s2n_inititalise_pthread_atfork(void)
  133. {
  134. /* Register the fork handler pthread_atfork_on_fork that is executed in the
  135. * child process after a fork.
  136. */
  137. if (s2n_is_pthread_atfork_supported() == true) {
  138. RESULT_ENSURE(pthread_atfork(NULL, NULL, s2n_pthread_atfork_on_fork) == 0, S2N_ERR_FORK_DETECTION_INIT);
  139. }
  140. return S2N_RESULT_OK;
  141. }
  142. static S2N_RESULT s2n_initialise_fork_detection_methods_try(void *addr, long page_size)
  143. {
  144. RESULT_GUARD_PTR(addr);
  145. /* Some systems don't define MADV_WIPEONFORK in sys/mman.h but the kernel
  146. * still supports the mechanism (AL2 being a prime example). Likely because
  147. * glibc on the system is old. We might be able to include kernel header
  148. * files directly, that define MADV_WIPEONFORK, conditioning on specific
  149. * OS's. But it is a mess. A more reliable method is to probe the system, at
  150. * run-time, whether madvise supports the MADV_WIPEONFORK advice. However,
  151. * the method to probe for this feature is equivalent to actually attempting
  152. * to initialise the MADV_WIPEONFORK fork detection. Compare with
  153. * probe_madv_wipeonfork_support() (used for testing).
  154. *
  155. * Instead, we apply best-effort to initialise the MADV_WIPEONFORK fork
  156. * detection and otherwise always require pthread_atfork to be initialised.
  157. * We also currently always apply prediction resistance. So, this should be
  158. * a safe default.
  159. */
  160. if (ignore_wipeonfork_or_inherit_zero_method_for_testing == false) {
  161. RESULT_GUARD(s2n_initialise_wipeonfork_best_effort(addr, page_size));
  162. }
  163. if (ignore_wipeonfork_or_inherit_zero_method_for_testing == false) {
  164. RESULT_GUARD(s2n_initialise_inherit_zero(addr, page_size));
  165. }
  166. if (ignore_pthread_atfork_method_for_testing == false) {
  167. RESULT_GUARD(s2n_inititalise_pthread_atfork());
  168. }
  169. fgn_state.zero_on_fork_addr = addr;
  170. *fgn_state.zero_on_fork_addr = S2N_NO_FORK_EVENT;
  171. fgn_state.is_fork_detection_enabled = true;
  172. return S2N_RESULT_OK;
  173. }
  174. static S2N_RESULT s2n_setup_mapping(void **addr, long *page_size)
  175. {
  176. *page_size = sysconf(_SC_PAGESIZE);
  177. RESULT_ENSURE_GT(*page_size, 0);
  178. *addr = mmap(NULL, (size_t) *page_size, PROT_READ | PROT_WRITE,
  179. MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  180. RESULT_ENSURE_NE(*addr, MAP_FAILED);
  181. return S2N_RESULT_OK;
  182. }
  183. static void s2n_initialise_fork_detection_methods(void)
  184. {
  185. void *addr = MAP_FAILED;
  186. long page_size = 0;
  187. /* Only used to disable fork detection mechanisms during testing. */
  188. if (ignore_wipeonfork_or_inherit_zero_method_for_testing == true && ignore_pthread_atfork_method_for_testing == true) {
  189. ignore_fork_detection_for_testing = true;
  190. return;
  191. }
  192. if (s2n_result_is_error(s2n_setup_mapping(&addr, &page_size)) == true) {
  193. return;
  194. }
  195. /* Now we know that we have some memory mapped. Try to initialise fork
  196. * detection methods. Unmap the memory if we fail for some reason.
  197. */
  198. if (s2n_result_is_error(s2n_initialise_fork_detection_methods_try(addr, page_size)) == true) {
  199. /* No reason to verify return value of munmap() since we can't use that
  200. * information for anything anyway. */
  201. munmap(addr, (size_t) page_size);
  202. addr = NULL;
  203. fgn_state.zero_on_fork_addr = NULL;
  204. fgn_state.is_fork_detection_enabled = false;
  205. }
  206. }
  207. /* s2n_get_fork_generation_number returns S2N_RESULT_OK on success and
  208. * S2N_RESULT_ERROR otherwise.
  209. *
  210. * On success, returns the current fork generation number in
  211. * return_fork_generation_number. Caller must synchronise access to
  212. * return_fork_generation_number.
  213. */
  214. S2N_RESULT s2n_get_fork_generation_number(uint64_t *return_fork_generation_number)
  215. {
  216. RESULT_ENSURE(pthread_once(&fgn_state.fork_detection_once, s2n_initialise_fork_detection_methods) == 0, S2N_ERR_FORK_DETECTION_INIT);
  217. if (ignore_fork_detection_for_testing == true) {
  218. /* Fork detection is meant to be disabled. Hence, return success.
  219. * This should only happen during testing.
  220. */
  221. RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST);
  222. return S2N_RESULT_OK;
  223. }
  224. RESULT_ENSURE(fgn_state.is_fork_detection_enabled == true, S2N_ERR_FORK_DETECTION_INIT);
  225. /* In most cases, we would not need to increment the fork generation number.
  226. * So, it is cheaper, in the expected case, to take an optimistic read lock
  227. * and later aquire a write lock if needed.
  228. * Note that we set the returned fgn before checking for a fork event. We
  229. * need to do this because thread execution might change between releasing
  230. * the read lock and taking the write lock. In that time span, another
  231. * thread can reset the fork event detection sentinel and we return from
  232. * s2n_get_fork_generation_number() without setting the returned fgn
  233. * appropriately.
  234. */
  235. RESULT_ENSURE(pthread_rwlock_rdlock(&fgn_state.fork_detection_rw_lock) == 0, S2N_ERR_RETRIEVE_FORK_GENERATION_NUMBER);
  236. *return_fork_generation_number = fgn_state.current_fork_generation_number;
  237. if (*fgn_state.zero_on_fork_addr != S2N_FORK_EVENT) {
  238. /* No fork event detected. */
  239. RESULT_ENSURE(pthread_rwlock_unlock(&fgn_state.fork_detection_rw_lock) == 0, S2N_ERR_RETRIEVE_FORK_GENERATION_NUMBER);
  240. return S2N_RESULT_OK;
  241. }
  242. RESULT_ENSURE(pthread_rwlock_unlock(&fgn_state.fork_detection_rw_lock) == 0, S2N_ERR_RETRIEVE_FORK_GENERATION_NUMBER);
  243. /* We are mutating the process-global, cached fork generation number. Need
  244. * to acquire the write lock for that. Set returned fgn before checking the
  245. * if condition with the same reasons as above.
  246. */
  247. RESULT_ENSURE(pthread_rwlock_wrlock(&fgn_state.fork_detection_rw_lock) == 0, S2N_ERR_RETRIEVE_FORK_GENERATION_NUMBER);
  248. *return_fork_generation_number = fgn_state.current_fork_generation_number;
  249. if (*fgn_state.zero_on_fork_addr == S2N_FORK_EVENT) {
  250. /* Fork event has been detected; reset sentinel, increment cached fork
  251. * generation number (which is now "current" in this child process), and
  252. * write incremented fork generation number to the output parameter.
  253. */
  254. *fgn_state.zero_on_fork_addr = S2N_NO_FORK_EVENT;
  255. fgn_state.current_fork_generation_number = fgn_state.current_fork_generation_number + 1;
  256. *return_fork_generation_number = fgn_state.current_fork_generation_number;
  257. }
  258. RESULT_ENSURE(pthread_rwlock_unlock(&fgn_state.fork_detection_rw_lock) == 0, S2N_ERR_RETRIEVE_FORK_GENERATION_NUMBER);
  259. return S2N_RESULT_OK;
  260. }
  261. static void s2n_cleanup_cb_munmap(void **probe_addr)
  262. {
  263. munmap(*probe_addr, (size_t) sysconf(_SC_PAGESIZE));
  264. }
  265. /* Run-time probe checking whether the system supports the MADV_WIPEONFORK fork
  266. * detection mechanism.
  267. */
  268. static S2N_RESULT s2n_probe_madv_wipeonfork_support(void)
  269. {
  270. bool result = false;
  271. /* It is not an error to call munmap on a range that does not contain any
  272. * mapped pages.
  273. */
  274. DEFER_CLEANUP(void *probe_addr = MAP_FAILED, s2n_cleanup_cb_munmap);
  275. long page_size = 0;
  276. RESULT_GUARD(s2n_setup_mapping(&probe_addr, &page_size));
  277. #if defined(S2N_MADVISE_SUPPORTED)
  278. /* Some versions of qemu (up to at least 5.0.0-rc4, see
  279. * linux-user/syscall.c) ignore invalid advice arguments. Hence, we first
  280. * verify that madvise() rejects advice arguments it doesn't know about.
  281. */
  282. RESULT_ENSURE_NE(madvise(probe_addr, (size_t) page_size, -1), 0);
  283. RESULT_ENSURE_EQ(madvise(probe_addr, (size_t) page_size, MADV_WIPEONFORK), 0);
  284. result = true;
  285. #endif
  286. RESULT_ENSURE_EQ(result, true);
  287. return S2N_RESULT_OK;
  288. }
  289. bool s2n_is_madv_wipeonfork_supported(void)
  290. {
  291. return s2n_result_is_ok(s2n_probe_madv_wipeonfork_support());
  292. }
  293. bool s2n_is_map_inherit_zero_supported(void)
  294. {
  295. #if defined(S2N_MINHERIT_SUPPORTED) && defined(MAP_INHERIT_ZERO)
  296. return true;
  297. #else
  298. return false;
  299. #endif
  300. }
  301. bool s2n_is_pthread_atfork_supported(void)
  302. {
  303. /*
  304. * There is a bug in OpenBSD's libc which is triggered by
  305. * multi-generational forking of multi-threaded processes which call
  306. * pthread_atfork(3). Under these conditions, a grandchild process will
  307. * deadlock when trying to fork a great-grandchild.
  308. * https://marc.info/?l=openbsd-tech&m=167047636422884&w=2
  309. */
  310. #if defined(__OpenBSD__)
  311. return false;
  312. #else
  313. return true;
  314. #endif
  315. }
  316. /* Use for testing only */
  317. S2N_RESULT s2n_ignore_wipeonfork_and_inherit_zero_for_testing(void)
  318. {
  319. RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST);
  320. ignore_wipeonfork_or_inherit_zero_method_for_testing = true;
  321. return S2N_RESULT_OK;
  322. }
  323. S2N_RESULT s2n_ignore_pthread_atfork_for_testing(void)
  324. {
  325. RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST);
  326. ignore_pthread_atfork_method_for_testing = true;
  327. return S2N_RESULT_OK;
  328. }