B&W print-friendly diagrams + tinkerpop-0001 + wave-3 proof sections. Squash of 94 local commits onto remote master.
2.5 KiB
| id | repo | file | line | status | severity | complexity | pattern |
|---|---|---|---|---|---|---|---|
| tor-0001 | torproject/tor | src/feature/nodelist/routerlist.c | 2179 | unpatched | MEDIUM | O(R²) — R = number of router descriptors in batch; shrinks as items removed | smartlist_contains_string on smartlist_t inside SMARTLIST_FOREACH_BEGIN over same-size list |
Description
router_load_routers_from_string() validates received router descriptors against a
list of requested fingerprints using smartlist_contains_string:
SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
if (requested_fingerprints) {
base16_encode(fp, ...);
if (smartlist_contains_string(requested_fingerprints, fp)) { // O(|fp_list|)
smartlist_string_remove(requested_fingerprints, fp);
}
...
}
} SMARTLIST_FOREACH_END(ri);
smartlist_contains_string (smartlist.c:97) is a linear scan:
for (i=0; i < sl->num_used; i++)
if (strcmp((const char*)sl->list[i], element) == 0)
return 1;
requested_fingerprints starts at size R (= len(routers)). Each match removes one entry,
so total comparisons = R + (R-1) + ... = O(R²/2). Same pattern occurs in the
extrainfo path at line 2263-2295.
Call context
router_load_routers_from_string() is called when processing downloaded router
descriptors from directory servers. For directory caches and authorities, R can be
in the thousands (7,000-8,000 relays in the full consensus).
Activation: any Tor node that fetches router descriptors — which is every relay and every client at startup and during periodic refresh.
Fix
Replace smartlist_t *requested_fingerprints (hex strings) with digestmap_t * (raw
20-byte digest → dummy pointer). Tor already uses digestmap_t extensively in the
same file (lines 2689, 2717, 2802). Fix:
// Before: smartlist_t *requested_fingerprints (hex strings, O(n) contains)
// After: digestmap_t *requested_fingerprints (raw digests, O(1) lookup)
// Lookup:
if (digestmap_get(requested_fingerprints, ri->cache_info.signed_descriptor_digest)) {
digestmap_remove(requested_fingerprints, ri->cache_info.signed_descriptor_digest);
}
The hex encoding step (base16_encode) can be removed since we key on raw bytes.
smartlist_string_remove calls also eliminated. Apply to both routers path (line
2179) and invalid_digests / extrainfo_list paths (lines 2216, 2295).
Work items
- patch
- unit test (operation count: contains calls before/after on batch R=1000)
- integration test (tor relay startup, descriptor fetch)