summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2026-10-30 01:00:52 +0100
committerDirk Engling <erdgeist@erdgeist.org>2026-10-30 01:00:52 +0100
commit79f4110365fe85f4377c50f0893134a8d5e45575 (patch)
treee69f5ceae67e4f283bede83f6649b7df0ded7076 /tests
parentd92945fe3a7960cfc7ddfd70a9a37d6db666c1e2 (diff)
Consolidate all tcp/http tear down into a single http_close function to prevent a rare UAF for expiring connections
Diffstat (limited to 'tests')
-rw-r--r--tests/integration/test_expiry.py42
1 files changed, 42 insertions, 0 deletions
diff --git a/tests/integration/test_expiry.py b/tests/integration/test_expiry.py
new file mode 100644
index 0000000..4466724
--- /dev/null
+++ b/tests/integration/test_expiry.py
@@ -0,0 +1,42 @@
1"""What the expiry sweep finds after a connection has already gone.
2
3A plain request is answered and the connection closed by http_senddata, not
4by the sweep. If that path leaves the descriptor's slot behind, the sweep
5walks into it once the client timeout has passed and frees a cookie that is
6already gone. Under the sanitizer build that is reported, and teardown turns
7it into a failure.
8
9The two numbers below mirror OT_CLIENT_TIMEOUT and
10OT_CLIENT_TIMEOUT_CHECKINTERVAL in FEATURES_integration.
11"""
12
13import time
14import unittest
15
16from harness import TrackerTestCase
17
18CLIENT_TIMEOUT = 5
19CHECK_INTERVAL = 1
20
21
22class ExpirySweep(TrackerTestCase):
23 def test_an_answered_request_leaves_nothing_behind(self):
24 info_hash = self.hash_for("expiry-sweep")
25 self.assertEqual(self.tracker.announce(info_hash)[b"complete"], 1)
26
27 # Long enough for the slot to expire and for a sweep to visit it
28 time.sleep(CLIENT_TIMEOUT + 3 * CHECK_INTERVAL)
29
30 self.assertEqual(self.tracker.torrent_count(), 1, "the tracker stopped serving while sweeping")
31
32 def test_a_connection_that_never_speaks_is_swept(self):
33 """The other side of it: a slot that really is live must still go."""
34 import socket
35
36 idle = socket.create_connection(("127.0.0.1", self.tracker.port), timeout=5)
37 try:
38 time.sleep(CLIENT_TIMEOUT + 3 * CHECK_INTERVAL)
39 idle.settimeout(5)
40 self.assertEqual(idle.recv(64), b"", "the tracker held a connection that never sent a request")
41 finally:
42 idle.close()