项目文件夹

文件
wehub-resource-sync 3454a55636
cffconvert / validate (push) Has been cancelled
ci-workflow / pre-commit (push) Has been cancelled
ci-workflow / Minimal NLTK Download Test (macos-latest) (push) Has been cancelled
ci-workflow / Minimal NLTK Download Test (ubuntu-latest) (push) Has been cancelled
ci-workflow / Minimal NLTK Download Test (windows-latest) (push) Has been cancelled
ci-workflow / Python 3.10 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.11 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.12 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.13 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.14 on macos-latest (push) Has been cancelled
ci-workflow / Python 3.14t on macos-latest (push) Has been cancelled
ci-workflow / Python 3.10 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.11 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.12 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.13 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.14 on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.14t on ubuntu-latest (push) Has been cancelled
ci-workflow / Python 3.10 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.11 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.12 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.13 on windows-latest (push) Has been cancelled
ci-workflow / Python 3.14 on windows-latest (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:46:15 +08:00

88 行
3.0 KiB
Python

"""Regression tests for nltk.metrics.ConfusionMatrix (CWE-770; CVE-2026-12839).
A dense V x V matrix (V = number of distinct labels) was allocated and retained,
so an all-distinct input forced O(V**2) memory and OOM-killed the worker. The
matrix is now a sparse dict keyed by the observed (reference index, test index)
pairs. These tests confirm the storage is sparse and the public API is preserved.
The allocation test runs in a spawned process with a hard timeout so a regression
to the dense allocation cannot OOM or hang the rest of the suite.
"""
import multiprocessing
import queue
from nltk.metrics import ConfusionMatrix
_REF = "DET NN VB DET JJ NN NN IN DET NN".split()
_TEST = "DET VB VB DET NN NN NN IN DET NN".split()
def test_storage_is_sparse():
cm = ConfusionMatrix(_REF, _TEST)
assert isinstance(cm._confusion, dict)
# one entry per observed (ref, test) pair, not len(values) ** 2
observed = {(r, t) for r, t in zip(_REF, _TEST)}
assert len(cm._confusion) == len(observed)
def test_public_api_preserved():
cm = ConfusionMatrix(_REF, _TEST)
assert cm["NN", "NN"] == 3
assert cm["DET", "VB"] == 0 # a pair that never occurs
assert cm.recall("NN") == 0.75
assert cm.precision("NN") == 0.75
assert repr(cm) == "<ConfusionMatrix: 8/10 correct>"
# pretty_format still renders (exact output is covered by metrics.doctest)
assert "(row = reference; col = test)" in cm.pretty_format()
# A dense matrix at this size costs ~200 MB (V**2 cells); the sparse map costs a
# few MB. The threshold separates the two without attempting a multi-GB
# allocation when regressed.
_N = 5000
_MAX_BYTES = 50_000_000
_TIMEOUT = 15
def _alloc_worker(result_q):
try:
import tracemalloc
ref = [str(i) for i in range(_N)]
test = [str(_N - 1 - i) for i in range(_N)]
tracemalloc.start()
ConfusionMatrix(ref, test) # retains self._confusion
_cur, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
result_q.put(("ok", peak))
except BaseException as exc: # surface to the parent process
result_q.put(("error", repr(exc)))
def _run_in_process(target):
ctx = multiprocessing.get_context("spawn")
result_q = ctx.Queue()
proc = ctx.Process(target=target, args=(result_q,))
proc.start()
proc.join(_TIMEOUT)
if proc.is_alive():
proc.terminate()
proc.join()
return False, None, None
try:
status, payload = result_q.get_nowait()
except queue.Empty:
return True, "error", "worker produced no result"
return True, status, payload
def test_all_distinct_input_does_not_allocate_dense_matrix():
"""An all-distinct sequence must not allocate a dense V x V matrix."""
finished, status, peak = _run_in_process(_alloc_worker)
assert finished, "ConfusionMatrix did not finish (dense V x V allocation OOM/hang)"
assert status == "ok", f"worker raised: {peak}"
assert (
peak < _MAX_BYTES
), f"allocated {peak/1e6:.0f} MB for {_N} distinct labels (DoS)"