• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/test_util/socket_use_after_close_detector.h"
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <grpc/support/port_platform.h>
24 #include <string.h>
25 
26 // IWYU pragma: no_include <arpa/inet.h>
27 // IWYU pragma: no_include <unistd.h>
28 
29 #include <grpc/support/sync.h>
30 
31 #include <algorithm>
32 #include <memory>
33 #include <string>
34 #include <thread>
35 #include <vector>
36 
37 #include "absl/log/log.h"
38 #include "gtest/gtest.h"
39 #include "src/core/lib/iomgr/sockaddr.h"
40 #include "test/core/test_util/port.h"
41 
42 #define BAD_SOCKET_RETURN_VAL (-1)
43 
44 namespace {
45 
46 #ifdef GPR_WINDOWS
OpenAndCloseSocketsStressLoop(int port,gpr_event * done_ev)47 void OpenAndCloseSocketsStressLoop(int port, gpr_event* done_ev) {
48   // TODO(apolcyn): re-enable this on windows if we can debug the failure.
49   // Previously, this was causing test flakes for a while b/c bind calls
50   // would fail with WSAEACCESS. Not clear if we were just making windows
51   // unhappy.
52   LOG(INFO) << "OpenAndCloseSocketsStressLoop is a no-op for windows";
53   return;
54 }
55 #else
56 void OpenAndCloseSocketsStressLoop(int port, gpr_event* done_ev) {
57   // The goal of this loop is to catch socket
58   // "use after close" bugs within the c-ares resolver by acting
59   // like some separate thread doing I/O.
60   // It's goal is to try to hit race conditions whereby:
61   //    1) The c-ares resolver closes a socket.
62   //    2) This loop opens a socket with (coincidentally) the same handle.
63   //    3) the c-ares resolver mistakenly uses that same socket without
64   //       realizing that its closed.
65   //    4) This loop performs an operation on that socket that should
66   //       succeed but instead fails because of what the c-ares
67   //       resolver did in the meantime.
68   sockaddr_in6 addr;
69   memset(&addr, 0, sizeof(addr));
70   addr.sin6_family = AF_INET6;
71   addr.sin6_port = htons(port);
72   (reinterpret_cast<char*>(&addr.sin6_addr))[15] = 1;
73   for (;;) {
74     if (gpr_event_get(done_ev)) {
75       return;
76     }
77     std::vector<int> sockets;
78     // First open a bunch of sockets, bind and listen
79     // '50' is an arbitrary number that, experimentally,
80     // has a good chance of catching bugs.
81     for (size_t i = 0; i < 50; i++) {
82       int s = socket(AF_INET6, SOCK_STREAM, 0);
83       int val = 1;
84       ASSERT_TRUE(setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) ==
85                   0)
86           << "Failed to set socketopt reuseport";
87       ASSERT_TRUE(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) ==
88                   0)
89           << "Failed to set socket reuseaddr";
90       ASSERT_TRUE(fcntl(s, F_SETFL, O_NONBLOCK) == 0)
91           << "Failed to set socket non-blocking";
92       ASSERT_TRUE(s != BAD_SOCKET_RETURN_VAL)
93           << "Failed to create TCP ipv6 socket";
94       ASSERT_TRUE(bind(s, (const sockaddr*)&addr, sizeof(addr)) == 0)
95           << "Failed to bind socket " + std::to_string(s) +
96                  " to [::1]:" + std::to_string(port) +
97                  ". errno: " + std::to_string(errno);
98       ASSERT_TRUE(listen(s, 1) == 0) << "Failed to listen on socket " +
99                                             std::to_string(s) +
100                                             ". errno: " + std::to_string(errno);
101       sockets.push_back(s);
102     }
103     // Do a non-blocking accept followed by a close on all of those sockets.
104     // Do this in a separate loop to try to induce a time window to hit races.
105     for (size_t i = 0; i < sockets.size(); i++) {
106       if (accept(sockets[i], nullptr, nullptr)) {
107         // If e.g. a "shutdown" was called on this fd from another thread,
108         // then this accept call should fail with an unexpected error.
109         ASSERT_TRUE(errno == EAGAIN || errno == EWOULDBLOCK)
110             << "OpenAndCloseSocketsStressLoop accept on socket " +
111                    std::to_string(sockets[i]) +
112                    " failed in "
113                    "an unexpected way. "
114                    "errno: " +
115                    std::to_string(errno) +
116                    ". Socket use-after-close bugs are likely.";
117       }
118       ASSERT_TRUE(close(sockets[i]) == 0)
119           << "Failed to close socket: " + std::to_string(sockets[i]) +
120                  ". errno: " + std::to_string(errno);
121     }
122   }
123 }
124 #endif
125 
126 }  // namespace
127 
128 namespace grpc_core {
129 namespace testing {
130 
SocketUseAfterCloseDetector()131 SocketUseAfterCloseDetector::SocketUseAfterCloseDetector() {
132   int port = grpc_pick_unused_port_or_die();
133   gpr_event_init(&done_ev_);
134   thread_ = std::make_unique<std::thread>(OpenAndCloseSocketsStressLoop, port,
135                                           &done_ev_);
136 }
137 
~SocketUseAfterCloseDetector()138 SocketUseAfterCloseDetector::~SocketUseAfterCloseDetector() {
139   gpr_event_set(&done_ev_, reinterpret_cast<void*>(1));
140   thread_->join();
141 }
142 
143 }  // namespace testing
144 }  // namespace grpc_core
145