summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2026-11-07 13:30:17 +0100
committerDirk Engling <erdgeist@erdgeist.org>2026-11-07 13:30:17 +0100
commitcfbe53da5d9ef8dad1bb3276c0b33ec2ab6e974b (patch)
treec553990fd398d9d20dab09df5ecdcba1da71264e
parentbbfd674e08c6b1fa4081f9c678fd220bead749d7 (diff)
Fix s24s stats to ensure no unallocated vector is dereferenced. Drive-by fix for the ip= scanner in the http parser
-rw-r--r--src/ot_http.c4
-rw-r--r--src/ot_stats.c6
-rw-r--r--tests/integration/test_stats.py9
3 files changed, 17 insertions, 2 deletions
diff --git a/src/ot_http.c b/src/ot_http.c
index 1b5281c..262936e 100644
--- a/src/ot_http.c
+++ b/src/ot_http.c
@@ -692,8 +692,10 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
692 { 692 {
693 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; 693 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16;
694 len = urlencoded_parse(&read_ptr, tmp_buf2, ws->outbuf + G_OUTBUF_SIZE, SCAN_SEARCHPATH_VALUE); 694 len = urlencoded_parse(&read_ptr, tmp_buf2, ws->outbuf + G_OUTBUF_SIZE, SCAN_SEARCHPATH_VALUE);
695 if (len <= 0)
696 HTTPERROR_400_PARAM;
695 tmp_buf2[len] = 0; 697 tmp_buf2[len] = 0;
696 if ((len <= 0) || ip_parse(tmp_buf2, tmp_buf1) != (size_t)len) 698 if (ip_parse(tmp_buf2, tmp_buf1) != (size_t)len)
697 HTTPERROR_400_PARAM; 699 HTTPERROR_400_PARAM;
698 OT_SETIP(&ws->peer, tmp_buf1); 700 OT_SETIP(&ws->peer, tmp_buf1);
699 } break; 701 } break;
diff --git a/src/ot_stats.c b/src/ot_stats.c
index c50d4e3..9e2eabe 100644
--- a/src/ot_stats.c
+++ b/src/ot_stats.c
@@ -244,9 +244,13 @@ static void stats_slash24s_txt(char **reply, const char *reply_end, size_t amoun
244 ot_vector *torrents_list = mutex_bucket_lock(bucket); 244 ot_vector *torrents_list = mutex_bucket_lock(bucket);
245 for (i = 0; i < torrents_list->size; ++i) { 245 for (i = 0; i < torrents_list->size; ++i) {
246 ot_peerlist *peer_list = (((ot_torrent *)(torrents_list->data))[i]).peer_list4; 246 ot_peerlist *peer_list = (((ot_torrent *)(torrents_list->data))[i]).peer_list4;
247 ot_vector *bucket_list = &peer_list->peers; 247 ot_vector *bucket_list;
248 int num_buckets = 1; 248 int num_buckets = 1;
249 249
250 if (!peer_list)
251 continue;
252 bucket_list = &peer_list->peers;
253
250 if (OT_PEERLIST_HASBUCKETS(peer_list)) { 254 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
251 num_buckets = bucket_list->size; 255 num_buckets = bucket_list->size;
252 bucket_list = (ot_vector *)bucket_list->data; 256 bucket_list = (ot_vector *)bucket_list->data;
diff --git a/tests/integration/test_stats.py b/tests/integration/test_stats.py
index 088d8a1..c4ac95a 100644
--- a/tests/integration/test_stats.py
+++ b/tests/integration/test_stats.py
@@ -177,6 +177,15 @@ class StatsNetworks(TrackerTestCase):
177 for line in lines: 177 for line in lines:
178 self.assertEqual(int(line.split(b":")[0]), 3, "three peers from the one network") 178 self.assertEqual(int(line.split(b":")[0]), 3, "three peers from the one network")
179 179
180 def test_the_report_survives_a_torrent_with_no_v4_peers(self):
181 # A torrent whose only peer is v6 has no v4 peer list once the lists
182 # are allocated on demand. The s24 report walks the v4 list of every
183 # torrent, so it has to skip the absent one rather than follow a null
184 # pointer. Needs WANT_IP_FROM_QUERY_STRING to force the family; without
185 # it the peer is loopback v4 and the walk is never asked to skip.
186 self.tracker.announce(self.hash_for("s24v6only"), ip="2001:db8::1", port=6001, left=0)
187 self.assertIn(b"Networks, limit /24:", self.tracker.stats("s24s"))
188
180 189
181if __name__ == "__main__": 190if __name__ == "__main__":
182 unittest.main() 191 unittest.main()