fork_detect.c
Go to the documentation of this file.
1 /* Copyright (c) 2020, Google Inc.
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 <openssl/base.h>
20 
21 #include "fork_detect.h"
22 
23 #if defined(OPENSSL_LINUX)
24 #include <sys/mman.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 
28 #include <openssl/type_check.h>
29 
30 #include "../delocate.h"
31 #include "../../internal.h"
32 
33 
34 #if defined(MADV_WIPEONFORK)
35 OPENSSL_STATIC_ASSERT(MADV_WIPEONFORK == 18, "MADV_WIPEONFORK is not 18");
36 #else
37 #define MADV_WIPEONFORK 18
38 #endif
39 
40 DEFINE_STATIC_ONCE(g_fork_detect_once);
41 DEFINE_STATIC_MUTEX(g_fork_detect_lock);
42 DEFINE_BSS_GET(volatile char *, g_fork_detect_addr);
43 DEFINE_BSS_GET(uint64_t, g_fork_generation);
44 DEFINE_BSS_GET(int, g_ignore_madv_wipeonfork);
45 
46 static void init_fork_detect(void) {
47  if (*g_ignore_madv_wipeonfork_bss_get()) {
48  return;
49  }
50 
51  long page_size = sysconf(_SC_PAGESIZE);
52  if (page_size <= 0) {
53  return;
54  }
55 
56  void *addr = mmap(NULL, (size_t)page_size, PROT_READ | PROT_WRITE,
57  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
58  if (addr == MAP_FAILED) {
59  return;
60  }
61 
62  // Some versions of qemu (up to at least 5.0.0-rc4, see linux-user/syscall.c)
63  // ignore |madvise| calls and just return zero (i.e. success). But we need to
64  // know whether MADV_WIPEONFORK actually took effect. Therefore try an invalid
65  // call to check that the implementation of |madvise| is actually rejecting
66  // unknown |advice| values.
67  if (madvise(addr, (size_t)page_size, -1) == 0 ||
68  madvise(addr, (size_t)page_size, MADV_WIPEONFORK) != 0) {
69  munmap(addr, (size_t)page_size);
70  return;
71  }
72 
73  *((volatile char *) addr) = 1;
74  *g_fork_detect_addr_bss_get() = addr;
75  *g_fork_generation_bss_get() = 1;
76 }
77 
79  // In a single-threaded process, there are obviously no races because there's
80  // only a single mutator in the address space.
81  //
82  // In a multi-threaded environment, |CRYPTO_once| ensures that the flag byte
83  // is initialised atomically, even if multiple threads enter this function
84  // concurrently.
85  //
86  // In the limit, the kernel may clear WIPEONFORK pages while a multi-threaded
87  // process is running. (For example, because a VM was cloned.) Therefore a
88  // lock is used below to synchronise the potentially multiple threads that may
89  // concurrently observe the cleared flag.
90 
91  CRYPTO_once(g_fork_detect_once_bss_get(), init_fork_detect);
92  // This pointer is |volatile| because the value pointed to may be changed by
93  // external forces (i.e. the kernel wiping the page) thus the compiler must
94  // not assume that it has exclusive access to it.
95  volatile char *const flag_ptr = *g_fork_detect_addr_bss_get();
96  if (flag_ptr == NULL) {
97  // Our kernel is too old to support |MADV_WIPEONFORK|.
98  return 0;
99  }
100 
101  struct CRYPTO_STATIC_MUTEX *const lock = g_fork_detect_lock_bss_get();
102  uint64_t *const generation_ptr = g_fork_generation_bss_get();
103 
105  uint64_t current_generation = *generation_ptr;
106  if (*flag_ptr) {
108  return current_generation;
109  }
110 
113  current_generation = *generation_ptr;
114  if (*flag_ptr == 0) {
115  // A fork has occurred.
116  *flag_ptr = 1;
117 
118  current_generation++;
119  if (current_generation == 0) {
120  current_generation = 1;
121  }
122  *generation_ptr = current_generation;
123  }
125 
126  return current_generation;
127 }
128 
130  *g_ignore_madv_wipeonfork_bss_get() = 1;
131 }
132 
133 #else // !OPENSSL_LINUX
134 
136 
137 #endif // OPENSSL_LINUX
DEFINE_STATIC_ONCE
#define DEFINE_STATIC_ONCE(name)
Definition: delocate.h:40
CRYPTO_STATIC_MUTEX_unlock_write
#define CRYPTO_STATIC_MUTEX_unlock_write
Definition: boringssl_prefix_symbols.h:1135
CRYPTO_once
#define CRYPTO_once
Definition: boringssl_prefix_symbols.h:1182
base.h
CRYPTO_get_fork_generation
uint64_t CRYPTO_get_fork_generation(void)
Definition: fork_detect.c:135
CRYPTO_STATIC_MUTEX
Definition: third_party/boringssl-with-bazel/src/crypto/internal.h:533
CRYPTO_STATIC_MUTEX_unlock_read
#define CRYPTO_STATIC_MUTEX_unlock_read
Definition: boringssl_prefix_symbols.h:1134
uint64_t
unsigned __int64 uint64_t
Definition: stdint-msvc2008.h:90
CRYPTO_STATIC_MUTEX_lock_write
#define CRYPTO_STATIC_MUTEX_lock_write
Definition: boringssl_prefix_symbols.h:1133
CRYPTO_fork_detect_ignore_madv_wipeonfork_for_testing
OPENSSL_EXPORT void CRYPTO_fork_detect_ignore_madv_wipeonfork_for_testing(void)
DEFINE_STATIC_MUTEX
#define DEFINE_STATIC_MUTEX(name)
Definition: delocate.h:43
fork_detect.h
CRYPTO_STATIC_MUTEX_lock_read
#define CRYPTO_STATIC_MUTEX_lock_read
Definition: boringssl_prefix_symbols.h:1132
type_check.h
DEFINE_BSS_GET
#define DEFINE_BSS_GET(type, name)
Definition: delocate.h:37
addr
struct sockaddr_in addr
Definition: libuv/docs/code/tcp-echo-server/main.c:10
OPENSSL_STATIC_ASSERT
#define OPENSSL_STATIC_ASSERT(cond, msg)
Definition: type_check.h:75


grpc
Author(s):
autogenerated on Thu Mar 13 2025 02:59:22