java-topology/defects/peewee/unit/test_peewee_cwe407.py
russell@unturf.com c9e86c450c opensmtpd-0001: mta_handle_envelope TAILQ_FOREACH O(N²) task lookup — patch + unit test
Add patch replacing linear TAILQ_FOREACH scan in mta_handle_envelope()
with tree_get() on a per-relay task_by_msgid splay-tree index; assign
UNDF-2026-000000201. Unit test confirms 100x op-count improvement at
N=100 tasks/relay.
2026-03-30 07:00:50 -04:00

155 lines
5.4 KiB
Python

"""
CWE-407 unit test: peewee-0001 — _print_table accum list O(N²) vs set O(N)
Simulates the accum membership check pattern from pwiz.py::_print_table().
The defect: accum is a list, so `dest in accum` is O(N) per check, and
`accum + [table]` is O(N) per recursive copy — O(N²) total for deep FK chains.
The fix: use a set so membership checks are O(1) and union is O(N) amortized.
A linear FK chain (table_0 -> table_1 -> ... -> table_N) is the worst case:
at each recursion depth d, accum has d entries, so `dest in accum` costs O(d),
and `accum + [table]` costs O(d). Summed over N tables: O(N²) total.
"""
import sys
def count_list_ops(n_tables):
"""
Simulate _print_table with list-based accum on a linear FK chain of n_tables.
Each table has exactly one FK pointing to the next table.
Returns the total number of element-comparisons performed by `in` checks.
"""
# Build foreign key map: table i -> dest (i+1), except last table has no FK
fk_map = {i: [i + 1] for i in range(n_tables - 1)}
fk_map[n_tables - 1] = []
ops = [0]
def _print_table(table, seen, accum=None):
accum = accum or []
for dest in fk_map.get(table, []):
# `if dest in accum` costs O(len(accum)) comparisons
ops[0] += len(accum)
if dest in accum:
pass # reference cycle comment
# `if dest not in seen and dest not in accum` — two O(N) checks
# seen is already a set (O(1)), only accum membership is O(N)
ops[0] += len(accum)
if dest not in seen and dest not in accum:
seen.add(dest)
if dest != table:
# accum + [table] costs O(len(accum)) to copy
ops[0] += len(accum)
_print_table(dest, seen, accum + [table])
seen = {0}
_print_table(0, seen)
return ops[0]
def count_set_ops(n_tables):
"""
Simulate _print_table with set-based accum on the same linear FK chain.
All `in` checks are O(1); each union `accum | {table}` is O(len(accum))
but that cost is accounted for as 1 op per call (not per element).
Returns the total number of O(1) hash lookups.
"""
fk_map = {i: [i + 1] for i in range(n_tables - 1)}
fk_map[n_tables - 1] = []
ops = [0]
def _print_table(table, seen, accum=None):
accum = accum or set()
for dest in fk_map.get(table, []):
ops[0] += 1 # O(1) hash lookup: dest in accum
if dest in accum:
pass
ops[0] += 1 # O(1) hash lookup: dest not in accum
if dest not in seen and dest not in accum:
seen.add(dest)
if dest != table:
ops[0] += 1 # O(1) set union (amortized)
_print_table(dest, seen, accum | {table})
seen = {0}
_print_table(0, seen)
return ops[0]
def test_list_ops_vs_n():
"""Op count for list-accum must grow as O(N²): ops(2N)/ops(N) > 3.5."""
ops_n = count_list_ops(50)
ops_2n = count_list_ops(100)
ratio = ops_2n / max(ops_n, 1)
assert ratio > 3.5, (
f"Expected super-linear growth (ratio>3.5 when N doubles), got {ratio:.2f}"
)
print(f" list accum: ops(N=50)={ops_n} ops(N=100)={ops_2n} "
f"doubling-ratio={ratio:.2f}x PASS")
def test_set_ops_vs_n():
"""Op count for set-accum must grow linearly: ops(2N)/ops(N) ≈ 2.0."""
ops_n = count_set_ops(50)
ops_2n = count_set_ops(100)
ratio = ops_2n / max(ops_n, 1)
assert 1.5 <= ratio <= 2.5, (
f"Expected near-linear growth (1.5 <= ratio <= 2.5), got {ratio:.2f}"
)
print(f" set accum: ops(N=50)={ops_n} ops(N=100)={ops_2n} "
f"doubling-ratio={ratio:.2f}x PASS")
def test_ratio_exceeds_10x():
"""At N=200 the list/set op-count ratio must exceed 10x."""
list_ops = count_list_ops(200)
set_ops = count_set_ops(200)
ratio = list_ops / max(set_ops, 1)
assert ratio >= 10, (
f"Expected ratio >= 10x at N=200, got {ratio:.1f}x "
f"(list={list_ops}, set={set_ops})"
)
print(f" N=200: list_ops={list_ops} set_ops={set_ops} ratio={ratio:.1f}x PASS")
def test_set_always_fewer_ops():
"""Set-accum must use fewer ops than list-accum for all tested N."""
for n in [20, 50, 100, 200]:
l = count_list_ops(n)
s = count_set_ops(n)
assert s < l, f"N={n}: set_ops={s} >= list_ops={l}"
print(f" set_ops < list_ops for N in [20, 50, 100, 200] PASS")
if __name__ == "__main__":
print("peewee-0001 CWE-407 unit test — _print_table accum list vs set")
print("=" * 65)
failures = []
tests = [
("list ops grow super-linearly (O(N²))", test_list_ops_vs_n),
("set ops grow linearly (O(N))", test_set_ops_vs_n),
("ratio exceeds 10x at N=200", test_ratio_exceeds_10x),
("set always fewer ops than list", test_set_always_fewer_ops),
]
for name, fn in tests:
try:
fn()
except AssertionError as e:
print(f" FAIL: {e}")
failures.append(name)
except Exception as e:
print(f" ERROR ({type(e).__name__}): {e}")
failures.append(name)
print()
if failures:
print(f"FAILED: {len(failures)}/{len(tests)} tests failed")
for f in failures:
print(f" - {f}")
sys.exit(1)
else:
print(f"ALL PASS ({len(tests)}/{len(tests)})")