fork_detect_test.cc
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 #include <openssl/base.h>
16 
17 // TSAN cannot cope with this test and complains that "starting new threads
18 // after multi-threaded fork is not supported".
19 #if defined(OPENSSL_LINUX) && !defined(OPENSSL_TSAN)
20 #include <errno.h>
21 #include <inttypes.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include <functional>
27 
28 #if defined(OPENSSL_THREADS)
29 #include <thread>
30 #include <vector>
31 #endif
32 
33 #include <gtest/gtest.h>
34 
35 #include "fork_detect.h"
36 
37 
38 static pid_t WaitpidEINTR(pid_t pid, int *out_status, int options) {
39  pid_t ret;
40  do {
41  ret = waitpid(pid, out_status, options);
42  } while (ret < 0 && errno == EINTR);
43 
44  return ret;
45 }
46 
47 // The *InChild functions run inside a child process and must report errors via
48 // |stderr| and |_exit| rather than GTest.
49 
50 static void CheckGenerationInChild(const char *name, uint64_t expected) {
51  uint64_t generation = CRYPTO_get_fork_generation();
52  if (generation != expected) {
53  fprintf(stderr, "%s generation (#1) was %" PRIu64 ", wanted %" PRIu64 ".\n",
54  name, generation, expected);
55  _exit(1);
56  }
57 
58  // The generation should be stable.
59  generation = CRYPTO_get_fork_generation();
60  if (generation != expected) {
61  fprintf(stderr, "%s generation (#2) was %" PRIu64 ", wanted %" PRIu64 ".\n",
62  name, generation, expected);
63  _exit(1);
64  }
65 }
66 
67 // ForkInChild forks a child which runs |f|. If the child exits unsuccessfully,
68 // this function will also exit unsuccessfully.
69 static void ForkInChild(std::function<void()> f) {
70  fflush(stderr); // Avoid duplicating any buffered output.
71 
72  const pid_t pid = fork();
73  if (pid < 0) {
74  perror("fork");
75  _exit(1);
76  } else if (pid == 0) {
77  f();
78  _exit(0);
79  }
80 
81  // Wait for the child and pass its exit code up.
82  int status;
83  if (WaitpidEINTR(pid, &status, 0) < 0) {
84  perror("waitpid");
85  _exit(1);
86  }
87  if (!WIFEXITED(status)) {
88  fprintf(stderr, "Child did not exit cleanly.\n");
89  _exit(1);
90  }
91  if (WEXITSTATUS(status) != 0) {
92  // Pass the failure up.
93  _exit(WEXITSTATUS(status));
94  }
95 }
96 
97 TEST(ForkDetect, Test) {
99  if (start == 0) {
100  fprintf(stderr, "Fork detection not supported. Skipping test.\n");
101  return;
102  }
103 
104  // The fork generation should be stable.
106 
107  fflush(stderr);
108  const pid_t child = fork();
109 
110  if (child == 0) {
111  // Fork grandchildren before observing the fork generation. The
112  // grandchildren will observe |start| + 1.
113  for (int i = 0; i < 2; i++) {
114  ForkInChild([&] { CheckGenerationInChild("Grandchild", start + 1); });
115  }
116 
117  // Now the child also observes |start| + 1. This is fine because it has
118  // already diverged from the grandchild at this point.
119  CheckGenerationInChild("Child", start + 1);
120 
121  // Forked grandchildren will now observe |start| + 2.
122  for (int i = 0; i < 2; i++) {
123  ForkInChild([&] { CheckGenerationInChild("Grandchild", start + 2); });
124  }
125 
126 #if defined(OPENSSL_THREADS)
127  // The fork generation logic itself must be thread-safe. We test this in a
128  // child process to capture the actual fork detection. This segment is meant
129  // to be tested in TSan.
130  ForkInChild([&] {
131  std::vector<std::thread> threads(4);
132  for (int i = 0; i < 2; i++) {
133  for (auto &t : threads) {
134  t = std::thread(
135  [&] { CheckGenerationInChild("Grandchild thread", start + 2); });
136  }
137  for (auto &t : threads) {
138  t.join();
139  }
140  }
141  });
142 #endif // OPENSSL_THREADS
143 
144  // The child still observes |start| + 1.
145  CheckGenerationInChild("Child", start + 1);
146  _exit(0);
147  }
148 
149  ASSERT_GT(child, 0) << "Error in fork: " << strerror(errno);
150  int status;
151  ASSERT_EQ(child, WaitpidEINTR(child, &status, 0))
152  << "Error in waitpid: " << strerror(errno);
153  ASSERT_TRUE(WIFEXITED(status));
154  EXPECT_EQ(0, WEXITSTATUS(status)) << "Error in child process";
155 
156  // We still observe |start|.
158 }
159 
160 #endif // OPENSSL_LINUX && !OPENSSL_TSAN
CRYPTO_get_fork_generation
#define CRYPTO_get_fork_generation
Definition: boringssl_prefix_symbols.h:1166
string.h
options
double_dict options[]
Definition: capstone_test.c:55
status
absl::Status status
Definition: rls.cc:251
setup.name
name
Definition: setup.py:542
threads
static uv_thread_t * threads
Definition: threadpool.c:38
base.h
python_utils.port_server.stderr
stderr
Definition: port_server.py:51
EXPECT_EQ
#define EXPECT_EQ(a, b)
Definition: iomgr/time_averaged_stats_test.cc:27
start
static uint64_t start
Definition: benchmark-pound.c:74
autogen_x86imm.f
f
Definition: autogen_x86imm.py:9
TEST
#define TEST(name, init_size,...)
Definition: arena_test.cc:75
uint64_t
unsigned __int64 uint64_t
Definition: stdint-msvc2008.h:90
googletest-filter-unittest.child
child
Definition: bloaty/third_party/googletest/googletest/test/googletest-filter-unittest.py:62
absl::str_format_internal::LengthMod::t
@ t
ret
UniquePtr< SSL_SESSION > ret
Definition: ssl_x509.cc:1029
ASSERT_TRUE
#define ASSERT_TRUE(condition)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:1973
fork_detect.h
ASSERT_GT
#define ASSERT_GT(val1, val2)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:2076
function
std::function< bool(GrpcTool *, int, const char **, const CliCredentials &, GrpcToolOutputCallback)> function
Definition: grpc_tool.cc:250
thread
static uv_thread_t thread
Definition: test-async-null-cb.c:29
Test
Definition: hpack_parser_test.cc:43
errno.h
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
ASSERT_EQ
#define ASSERT_EQ(val1, val2)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:2056


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