1 /* Copyright 2020 The BoringSSL Authors
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15 #if !defined(_GNU_SOURCE)
16 #define _GNU_SOURCE // needed for madvise() and MAP_ANONYMOUS on Linux.
17 #endif
18
19 #include "../bcm_support.h"
20
21 #if defined(OPENSSL_FORK_DETECTION_MADVISE)
22 #include <assert.h>
23 #include <stdlib.h>
24 #include <sys/mman.h>
25 #include <unistd.h>
26 #if defined(MADV_WIPEONFORK)
27 static_assert(MADV_WIPEONFORK == 18, "MADV_WIPEONFORK is not 18");
28 #else
29 #define MADV_WIPEONFORK 18
30 #endif
31 #elif defined(OPENSSL_FORK_DETECTION_PTHREAD_ATFORK)
32 #include <pthread.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #endif // OPENSSL_FORK_DETECTION_PTHREAD_ATFORK
36
37 #include "../internal.h"
38
39 #if defined(OPENSSL_FORK_DETECTION_MADVISE)
40 static int g_force_madv_wipeonfork;
41 static int g_force_madv_wipeonfork_enabled;
42 static CRYPTO_once_t g_fork_detect_once = CRYPTO_ONCE_INIT;
43 static CRYPTO_MUTEX g_fork_detect_lock = CRYPTO_MUTEX_INIT;
44 static CRYPTO_atomic_u32 *g_fork_detect_addr;
45 static uint64_t g_fork_generation;
46
init_fork_detect(void)47 static void init_fork_detect(void) {
48 if (g_force_madv_wipeonfork) {
49 return;
50 }
51
52 long page_size = sysconf(_SC_PAGESIZE);
53 if (page_size <= 0) {
54 return;
55 }
56
57 void *addr = mmap(NULL, (size_t)page_size, PROT_READ | PROT_WRITE,
58 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
59 if (addr == MAP_FAILED) {
60 return;
61 }
62
63 // Some versions of qemu (up to at least 5.0.0-rc4, see linux-user/syscall.c)
64 // ignore |madvise| calls and just return zero (i.e. success). But we need to
65 // know whether MADV_WIPEONFORK actually took effect. Therefore try an invalid
66 // call to check that the implementation of |madvise| is actually rejecting
67 // unknown |advice| values.
68 if (madvise(addr, (size_t)page_size, -1) == 0 ||
69 madvise(addr, (size_t)page_size, MADV_WIPEONFORK) != 0) {
70 munmap(addr, (size_t)page_size);
71 return;
72 }
73
74 CRYPTO_atomic_u32 *const atomic = reinterpret_cast<CRYPTO_atomic_u32 *>(addr);
75 CRYPTO_atomic_store_u32(atomic, 1);
76 g_fork_detect_addr = atomic;
77 g_fork_generation = 1;
78 }
79
CRYPTO_get_fork_generation(void)80 uint64_t CRYPTO_get_fork_generation(void) {
81 CRYPTO_once(&g_fork_detect_once, init_fork_detect);
82
83 // In a single-threaded process, there are obviously no races because there's
84 // only a single mutator in the address space.
85 //
86 // In a multi-threaded environment, |CRYPTO_once| ensures that the flag byte
87 // is initialised atomically, even if multiple threads enter this function
88 // concurrently.
89 //
90 // Additionally, while the kernel will only clear WIPEONFORK at a point when a
91 // child process is single-threaded, the child may become multi-threaded
92 // before it observes this. Therefore, we must synchronize the logic below.
93
94 CRYPTO_atomic_u32 *const flag_ptr = g_fork_detect_addr;
95 if (flag_ptr == NULL) {
96 // Our kernel is too old to support |MADV_WIPEONFORK| or
97 // |g_force_madv_wipeonfork| is set.
98 if (g_force_madv_wipeonfork && g_force_madv_wipeonfork_enabled) {
99 // A constant generation number to simulate support, even if the kernel
100 // doesn't support it.
101 return 42;
102 }
103 // With Linux and clone(), we do not believe that pthread_atfork() is
104 // sufficient for detecting all forms of address space duplication. At this
105 // point we have a kernel that does not support MADV_WIPEONFORK. We could
106 // return the generation number from pthread_atfork() here and it would
107 // probably be safe in almost any situation, but to ensure safety we return
108 // 0 and force an entropy draw on every call.
109 return 0;
110 }
111
112 // In the common case, try to observe the flag without taking a lock. This
113 // avoids cacheline contention in the PRNG.
114 uint64_t *const generation_ptr = &g_fork_generation;
115 if (CRYPTO_atomic_load_u32(flag_ptr) != 0) {
116 // If we observe a non-zero flag, it is safe to read |generation_ptr|
117 // without a lock. The flag and generation number are fixed for this copy of
118 // the address space.
119 return *generation_ptr;
120 }
121
122 // The flag was zero. The generation number must be incremented, but other
123 // threads may have concurrently observed the zero, so take a lock before
124 // incrementing.
125 CRYPTO_MUTEX *const lock = &g_fork_detect_lock;
126 CRYPTO_MUTEX_lock_write(lock);
127 uint64_t current_generation = *generation_ptr;
128 if (CRYPTO_atomic_load_u32(flag_ptr) == 0) {
129 // A fork has occurred.
130 current_generation++;
131 if (current_generation == 0) {
132 // Zero means fork detection isn't supported, so skip that value.
133 current_generation = 1;
134 }
135
136 // We must update |generation_ptr| before |flag_ptr|. Other threads may
137 // observe |flag_ptr| without taking a lock.
138 *generation_ptr = current_generation;
139 CRYPTO_atomic_store_u32(flag_ptr, 1);
140 }
141 CRYPTO_MUTEX_unlock_write(lock);
142
143 return current_generation;
144 }
145
CRYPTO_fork_detect_force_madv_wipeonfork_for_testing(int on)146 void CRYPTO_fork_detect_force_madv_wipeonfork_for_testing(int on) {
147 g_force_madv_wipeonfork = 1;
148 g_force_madv_wipeonfork_enabled = on;
149 }
150
151 #elif defined(OPENSSL_FORK_DETECTION_PTHREAD_ATFORK)
152
153 static CRYPTO_once_t g_pthread_fork_detection_once = CRYPTO_ONCE_INIT;
154 static uint64_t g_atfork_fork_generation;
155
we_are_forked(void)156 static void we_are_forked(void) {
157 // Immediately after a fork, the process must be single-threaded.
158 uint64_t value = g_atfork_fork_generation + 1;
159 if (value == 0) {
160 value = 1;
161 }
162 g_atfork_fork_generation = value;
163 }
164
init_pthread_fork_detection(void)165 static void init_pthread_fork_detection(void) {
166 if (pthread_atfork(NULL, NULL, we_are_forked) != 0) {
167 abort();
168 }
169 g_atfork_fork_generation = 1;
170 }
171
CRYPTO_get_fork_generation(void)172 uint64_t CRYPTO_get_fork_generation(void) {
173 CRYPTO_once(&g_pthread_fork_detection_once, init_pthread_fork_detection);
174
175 return g_atfork_fork_generation;
176 }
177
178 #elif defined(OPENSSL_DOES_NOT_FORK)
179
180 // These platforms are guaranteed not to fork, and therefore do not require
181 // fork detection support. Returning a constant non zero value makes BoringSSL
182 // assume address space duplication is not a concern and adding entropy to
183 // every RAND_bytes call is not needed.
CRYPTO_get_fork_generation(void)184 uint64_t CRYPTO_get_fork_generation(void) { return 0xc0ffee; }
185
186 #else
187
188 // These platforms may fork, but we do not have a mitigation mechanism in
189 // place. Returning a constant zero value makes BoringSSL assume that address
190 // space duplication could have occured on any call entropy must be added to
191 // every RAND_bytes call.
CRYPTO_get_fork_generation(void)192 uint64_t CRYPTO_get_fork_generation(void) { return 0; }
193
194 #endif
195