xref: /aosp_15_r20/external/grpc-grpc/test/core/util/socket_use_after_close_detector.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 //
2 //
3 // Copyright 2017 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include "test/core/util/socket_use_after_close_detector.h"
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <string.h>
24 
25 #include <grpc/support/port_platform.h>
26 
27 // IWYU pragma: no_include <arpa/inet.h>
28 // IWYU pragma: no_include <unistd.h>
29 
30 #include <algorithm>
31 #include <memory>
32 #include <string>
33 #include <thread>
34 #include <vector>
35 
36 #include "gtest/gtest.h"
37 
38 #include <grpc/support/sync.h>
39 
40 #include "src/core/lib/iomgr/sockaddr.h"
41 #include "test/core/util/port.h"
42 
43 // TODO(unknown): pull in different headers when enabling this
44 // test on windows. Also set BAD_SOCKET_RETURN_VAL
45 // to INVALID_SOCKET on windows.
46 #ifdef GPR_WINDOWS
47 #include "src/core/lib/iomgr/socket_windows.h"
48 #include "src/core/lib/iomgr/tcp_windows.h"
49 
50 #define BAD_SOCKET_RETURN_VAL INVALID_SOCKET
51 #else
52 #define BAD_SOCKET_RETURN_VAL (-1)
53 #endif
54 
55 namespace {
56 
57 #ifdef GPR_WINDOWS
OpenAndCloseSocketsStressLoop(int port,gpr_event * done_ev)58 void OpenAndCloseSocketsStressLoop(int port, gpr_event* done_ev) {
59   sockaddr_in6 addr;
60   memset(&addr, 0, sizeof(addr));
61   addr.sin6_family = AF_INET6;
62   addr.sin6_port = htons(port);
63   ((char*)&addr.sin6_addr)[15] = 1;
64   for (;;) {
65     if (gpr_event_get(done_ev)) {
66       return;
67     }
68     std::vector<int> sockets;
69     for (size_t i = 0; i < 50; i++) {
70       SOCKET s = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, nullptr, 0,
71                            WSA_FLAG_OVERLAPPED);
72       ASSERT_TRUE(s != BAD_SOCKET_RETURN_VAL)
73           << "Failed to create TCP ipv6 socket";
74       char val = 1;
75       ASSERT_TRUE(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) !=
76                   SOCKET_ERROR)
77           << "Failed to set socketopt reuseaddr. WSA error: " +
78                  std::to_string(WSAGetLastError());
79       ASSERT_TRUE(grpc_tcp_set_non_block(s) == absl::OkStatus())
80           << "Failed to set socket non-blocking";
81       ASSERT_TRUE(bind(s, (const sockaddr*)&addr, sizeof(addr)) != SOCKET_ERROR)
82           << "Failed to bind socket " + std::to_string(s) +
83                  " to [::1]:" + std::to_string(port) +
84                  ". WSA error: " + std::to_string(WSAGetLastError());
85       ASSERT_TRUE(listen(s, 1) != SOCKET_ERROR)
86           << "Failed to listen on socket " + std::to_string(s) +
87                  ". WSA error: " + std::to_string(WSAGetLastError());
88       sockets.push_back(s);
89     }
90     // Do a non-blocking accept followed by a close on all of those sockets.
91     // Do this in a separate loop to try to induce a time window to hit races.
92     for (size_t i = 0; i < sockets.size(); i++) {
93       ASSERT_TRUE(accept(sockets[i], nullptr, nullptr) == INVALID_SOCKET)
94           << "Accept on phony socket unexpectedly accepted actual connection.";
95       ASSERT_TRUE(WSAGetLastError() == WSAEWOULDBLOCK)
96           << "OpenAndCloseSocketsStressLoop accept on socket " +
97                  std::to_string(sockets[i]) +
98                  " failed in "
99                  "an unexpected way. "
100                  "WSA error: " +
101                  std::to_string(WSAGetLastError()) +
102                  ". Socket use-after-close bugs are likely.";
103       ASSERT_TRUE(closesocket(sockets[i]) != SOCKET_ERROR)
104           << "Failed to close socket: " + std::to_string(sockets[i]) +
105                  ". WSA error: " + std::to_string(WSAGetLastError());
106     }
107   }
108   return;
109 }
110 #else
111 void OpenAndCloseSocketsStressLoop(int port, gpr_event* done_ev) {
112   // The goal of this loop is to catch socket
113   // "use after close" bugs within the c-ares resolver by acting
114   // like some separate thread doing I/O.
115   // It's goal is to try to hit race conditions whereby:
116   //    1) The c-ares resolver closes a socket.
117   //    2) This loop opens a socket with (coincidentally) the same handle.
118   //    3) the c-ares resolver mistakenly uses that same socket without
119   //       realizing that its closed.
120   //    4) This loop performs an operation on that socket that should
121   //       succeed but instead fails because of what the c-ares
122   //       resolver did in the meantime.
123   sockaddr_in6 addr;
124   memset(&addr, 0, sizeof(addr));
125   addr.sin6_family = AF_INET6;
126   addr.sin6_port = htons(port);
127   (reinterpret_cast<char*>(&addr.sin6_addr))[15] = 1;
128   for (;;) {
129     if (gpr_event_get(done_ev)) {
130       return;
131     }
132     std::vector<int> sockets;
133     // First open a bunch of sockets, bind and listen
134     // '50' is an arbitrary number that, experimentally,
135     // has a good chance of catching bugs.
136     for (size_t i = 0; i < 50; i++) {
137       int s = socket(AF_INET6, SOCK_STREAM, 0);
138       int val = 1;
139       ASSERT_TRUE(setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) ==
140                   0)
141           << "Failed to set socketopt reuseport";
142       ASSERT_TRUE(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) ==
143                   0)
144           << "Failed to set socket reuseaddr";
145       ASSERT_TRUE(fcntl(s, F_SETFL, O_NONBLOCK) == 0)
146           << "Failed to set socket non-blocking";
147       ASSERT_TRUE(s != BAD_SOCKET_RETURN_VAL)
148           << "Failed to create TCP ipv6 socket";
149       ASSERT_TRUE(bind(s, (const sockaddr*)&addr, sizeof(addr)) == 0)
150           << "Failed to bind socket " + std::to_string(s) +
151                  " to [::1]:" + std::to_string(port) +
152                  ". errno: " + std::to_string(errno);
153       ASSERT_TRUE(listen(s, 1) == 0) << "Failed to listen on socket " +
154                                             std::to_string(s) +
155                                             ". errno: " + std::to_string(errno);
156       sockets.push_back(s);
157     }
158     // Do a non-blocking accept followed by a close on all of those sockets.
159     // Do this in a separate loop to try to induce a time window to hit races.
160     for (size_t i = 0; i < sockets.size(); i++) {
161       if (accept(sockets[i], nullptr, nullptr)) {
162         // If e.g. a "shutdown" was called on this fd from another thread,
163         // then this accept call should fail with an unexpected error.
164         ASSERT_TRUE(errno == EAGAIN || errno == EWOULDBLOCK)
165             << "OpenAndCloseSocketsStressLoop accept on socket " +
166                    std::to_string(sockets[i]) +
167                    " failed in "
168                    "an unexpected way. "
169                    "errno: " +
170                    std::to_string(errno) +
171                    ". Socket use-after-close bugs are likely.";
172       }
173       ASSERT_TRUE(close(sockets[i]) == 0)
174           << "Failed to close socket: " + std::to_string(sockets[i]) +
175                  ". errno: " + std::to_string(errno);
176     }
177   }
178 }
179 #endif
180 
181 }  // namespace
182 
183 namespace grpc_core {
184 namespace testing {
185 
SocketUseAfterCloseDetector()186 SocketUseAfterCloseDetector::SocketUseAfterCloseDetector() {
187   int port = grpc_pick_unused_port_or_die();
188   gpr_event_init(&done_ev_);
189   thread_ = std::make_unique<std::thread>(OpenAndCloseSocketsStressLoop, port,
190                                           &done_ev_);
191 }
192 
~SocketUseAfterCloseDetector()193 SocketUseAfterCloseDetector::~SocketUseAfterCloseDetector() {
194   gpr_event_set(&done_ev_, reinterpret_cast<void*>(1));
195   thread_->join();
196 }
197 
198 }  // namespace testing
199 }  // namespace grpc_core
200