summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2026-11-06 20:25:39 +0100
committerDirk Engling <erdgeist@erdgeist.org>2026-11-06 20:25:39 +0100
commit97e58519bc946daabcadc531510497c3598961a1 (patch)
tree74ecfb22d5666db86a1406b27313ffdf6f348879 /tests
parenta282b339504380bd2b1d705720ea2de757ae5541 (diff)
Fix an accesslist reload issue where an empty-but-present changeset after a reload could potentially walk a NULL array
Diffstat (limited to 'tests')
-rw-r--r--tests/integration/harness.py5
-rw-r--r--tests/integration/test_dynamic_accesslist.py18
2 files changed, 23 insertions, 0 deletions
diff --git a/tests/integration/harness.py b/tests/integration/harness.py
index e5ec621..f3cd0fa 100644
--- a/tests/integration/harness.py
+++ b/tests/integration/harness.py
@@ -135,6 +135,11 @@ class Tracker:
135 self.log.seek(0) 135 self.log.seek(0)
136 return self.log.read().decode("utf-8", "replace") 136 return self.log.read().decode("utf-8", "replace")
137 137
138 def reload(self):
139 """SIGHUP makes the accesslist reader re-read its files, which also
140 empties the dynamic add/delete changesets."""
141 os.kill(self.process.pid, signal.SIGHUP)
142
138 def stop(self): 143 def stop(self):
139 """Returns anything the tracker complained about on the way out. 144 """Returns anything the tracker complained about on the way out.
140 145
diff --git a/tests/integration/test_dynamic_accesslist.py b/tests/integration/test_dynamic_accesslist.py
index 7fa5fde..2072f5e 100644
--- a/tests/integration/test_dynamic_accesslist.py
+++ b/tests/integration/test_dynamic_accesslist.py
@@ -40,6 +40,24 @@ class DynamicAccesslist(TrackerTestCase):
40 time.sleep(0.05) 40 time.sleep(0.05)
41 return False 41 return False
42 42
43 def test_a_hash_added_after_a_reload_is_blocked(self):
44 """A reload leaves the add changeset an empty list rather than an
45 absent one. Adding against it walked a null array; the hash must be
46 blocked and the tracker must stay up."""
47 OTHER = bytes(range(21, 41))
48 self.feed("add.fifo", BLOCKED)
49 self.assertTrue(self.wait_for(BLOCKED, blocked=True), "the first hash was never blocked")
50
51 # The reload empties the add changeset, which unblocks the first hash.
52 # Wait for that before adding the second, so it lands on the empty list
53 # the bug walked, not on the changeset before the reload has cleared it.
54 self.tracker.reload()
55 self.assertTrue(self.wait_for(BLOCKED, blocked=False), "the reload never emptied the changeset")
56
57 self.feed("add.fifo", OTHER)
58 self.assertTrue(self.wait_for(OTHER, blocked=True), "the hash added after the reload was never blocked")
59 # tearDown's stop() surfaces any sanitizer complaint on the way out.
60
43 def test_a_hash_can_be_blocked_and_released_while_running(self): 61 def test_a_hash_can_be_blocked_and_released_while_running(self):
44 self.assertNotIn(b"failure reason", self.tracker.announce(BLOCKED)) 62 self.assertNotIn(b"failure reason", self.tracker.announce(BLOCKED))
45 63