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