Переглянути джерело

Use iptables to disable network (#2363)

* Use iptables to disable network

* Fix race condition problem

* Enable network after test finishes
yhirose 1 місяць тому
батько
коміт
c1ee85d89e
2 змінених файлів з 35 додано та 22 видалено
  1. 12 1
      .github/workflows/test_offline.yaml
  2. 23 21
      test/test.cc

+ 12 - 1
.github/workflows/test_offline.yaml

@@ -40,7 +40,13 @@ jobs:
         if: matrix.tls_backend == 'openssl'
         run: sudo apt-get install -y libssl-dev
       - name: disable network
-        run: sudo sh -c 'echo > /etc/resolv.conf'
+        run: |
+          sudo iptables -A OUTPUT -o lo -j ACCEPT
+          sudo iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
+          sudo iptables -A OUTPUT -j REJECT
+          sudo ip6tables -A OUTPUT -o lo -j ACCEPT
+          sudo ip6tables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
+          sudo ip6tables -A OUTPUT -j REJECT
       - name: build and run tests (OpenSSL)
         if: matrix.tls_backend == 'openssl'
         run: cd test && make
@@ -49,3 +55,8 @@ jobs:
       - name: build and run tests (No TLS)
         if: matrix.tls_backend == 'no-tls'
         run: cd test && make test_no_tls && ./test_no_tls
+      - name: restore network
+        if: always()
+        run: |
+          sudo iptables -F OUTPUT
+          sudo ip6tables -F OUTPUT

+ 23 - 21
test/test.cc

@@ -9114,29 +9114,31 @@ TEST(ClientVulnerabilityTest, ZipBombWithoutContentLength) {
   signal(SIGPIPE, SIG_IGN);
 #endif
 
-  auto server_thread = std::thread([&compressed] {
-    auto srv = ::socket(AF_INET, SOCK_STREAM, 0);
-    default_socket_options(srv);
-    detail::set_socket_opt_time(srv, SOL_SOCKET, SO_RCVTIMEO, 5, 0);
-    detail::set_socket_opt_time(srv, SOL_SOCKET, SO_SNDTIMEO, 5, 0);
-
-    sockaddr_in addr{};
-    addr.sin_family = AF_INET;
-    addr.sin_port = htons(PORT + 3);
-    ::inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
+  // Set up the listening socket in the main thread so the server is guaranteed
+  // to be ready before the client connects (eliminates race condition).
+  auto srv = ::socket(AF_INET, SOCK_STREAM, 0);
+  default_socket_options(srv);
+  detail::set_socket_opt_time(srv, SOL_SOCKET, SO_RCVTIMEO, 5, 0);
+  detail::set_socket_opt_time(srv, SOL_SOCKET, SO_SNDTIMEO, 5, 0);
+
+  sockaddr_in addr{};
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(PORT + 3);
+  ::inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
 
-    int opt = 1;
-    ::setsockopt(srv, SOL_SOCKET, SO_REUSEADDR,
+  int opt = 1;
+  ::setsockopt(srv, SOL_SOCKET, SO_REUSEADDR,
 #ifdef _WIN32
-                 reinterpret_cast<const char *>(&opt),
+               reinterpret_cast<const char *>(&opt),
 #else
-                 &opt,
+               &opt,
 #endif
-                 sizeof(opt));
+               sizeof(opt));
 
-    ::bind(srv, reinterpret_cast<sockaddr *>(&addr), sizeof(addr));
-    ::listen(srv, 1);
+  ASSERT_EQ(0, ::bind(srv, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)));
+  ASSERT_EQ(0, ::listen(srv, 1));
 
+  auto server_thread = std::thread([&compressed, srv] {
     sockaddr_in cli_addr{};
     socklen_t cli_len = sizeof(cli_addr);
     auto cli = ::accept(srv, reinterpret_cast<sockaddr *>(&cli_addr), &cli_len);
@@ -9180,10 +9182,12 @@ TEST(ClientVulnerabilityTest, ZipBombWithoutContentLength) {
 
       detail::close_socket(cli);
     }
-    detail::close_socket(srv);
   });
 
-  std::this_thread::sleep_for(std::chrono::milliseconds(200));
+  auto se = detail::scope_exit([&] {
+    detail::close_socket(srv);
+    server_thread.join();
+  });
 
   size_t total_decompressed = 0;
 
@@ -9204,8 +9208,6 @@ TEST(ClientVulnerabilityTest, ZipBombWithoutContentLength) {
     }
   }
 
-  server_thread.join();
-
   // The decompressed size must be capped by payload_max_length. Without
   // protection, the client would decompress the full 10MB from a tiny
   // compressed payload, enabling a zip bomb DoS attack.