summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ot_accesslist.c12
-rw-r--r--tests/integration/harness.py5
-rw-r--r--tests/integration/test_dynamic_accesslist.py18
3 files changed, 32 insertions, 3 deletions
diff --git a/src/ot_accesslist.c b/src/ot_accesslist.c
index 0597e9a..487597e 100644
--- a/src/ot_accesslist.c
+++ b/src/ot_accesslist.c
@@ -353,11 +353,17 @@ static void *accesslist_adddel_worker(const char *fifoname, ot_accesslist *_
353 /* Only if the info hash is not in the adding_to list, create a new head with that entry */ 353 /* Only if the info hash is not in the adding_to list, create a new head with that entry */
354 if (!exactmatch) { 354 if (!exactmatch) {
355 ot_accesslist *accesslist_new = accesslist_make(*adding_to, (*adding_to)->size + 1); 355 ot_accesslist *accesslist_new = accesslist_make(*adding_to, (*adding_to)->size + 1);
356 ptrdiff_t off = insert_point - (*adding_to)->list;
357 if (accesslist_new) { 356 if (accesslist_new) {
358 memcpy(accesslist_new->list, (*adding_to)->list, sizeof(ot_hash) * off); 357 ptrdiff_t off = (*adding_to)->size ? insert_point - (*adding_to)->list : 0;
358 size_t after = (*adding_to)->size - off;
359
360 /* A reload leaves an empty list with no array at all, and memcpy
361 may not be handed a null pointer even for nothing */
362 if (off)
363 memcpy(accesslist_new->list, (*adding_to)->list, sizeof(ot_hash) * off);
359 memcpy(accesslist_new->list + off, info_hash, sizeof(info_hash)); 364 memcpy(accesslist_new->list + off, info_hash, sizeof(info_hash));
360 memcpy(accesslist_new->list + off + 1, (*adding_to)->list + off, sizeof(ot_hash) * ((*adding_to)->size - off)); 365 if (after)
366 memcpy(accesslist_new->list + off + 1, (*adding_to)->list + off, sizeof(ot_hash) * after);
361 (*adding_to)->base = time_now_minutes(); 367 (*adding_to)->base = time_now_minutes();
362 *adding_to = accesslist_new; 368 *adding_to = accesslist_new;
363 } 369 }
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