Skip to content

🐛 fix(select): raise MemoryError on selector OOM - #1233

Merged
gaborbernat merged 1 commit into
tox-dev:mainfrom
gaborbernat:fix/alloc-selector-compile
Oct 8, 2026
Merged

gaborbernat merged 1 commit into
tox-dev:mainfrom
gaborbernat:fix/alloc-selector-compile

Conversation

@gaborbernat

Copy link
Copy Markdown
Member

An allocation failure while compiling a selector set the same flag as a syntax error. Failing the third PyMem allocation of document.select("main > p[zq] a, :is(b, i, [zq]) :has(> a)") raised SelectorSyntaxError ("unexpected token") for a valid selector. The same failure inside a forgiving :is() or :where() list dropped the arm as if it were invalid and returned a result. A failed build of the document's element index fell back to a full walk and hid the failure.

The parser now records an allocation failure as an error with no reason, which every caller, css_to_xpath() and the rewriter included, raises as MemoryError, and a forgiving list stops on it, since Selectors 4 §16.1 ignores only selectors that fail to parse. select(), select_one() and find_all() build the index before they walk and raise MemoryError when the build fails; the same check inside the walk cost select 3,000 instructions a call.

libxml2 2.16.0 gives a failed XPath compile its own XPATH_MEMORY_ERROR, and lexbor 3.1.0's CSS parser stops with LXB_STATUS_ERROR_MEMORY_ALLOCATION instead of reporting an unexpected token. Their Python bindings lose that: lxml 7.0.0b1 wraps every failed compile in XPathSyntaxError, and selectolax 1.0.0 raises SelectolaxError("Can't parse CSS selector."). Servo's selectors 0.41.0 uses Rust's infallible allocation, which aborts the process.

@gaborbernat gaborbernat added the bug Something isn't working label Oct 8, 2026
@gaborbernat
gaborbernat force-pushed the fix/alloc-selector-compile branch from c94a587 to 29f7ef8 Compare October 8, 2026 12:29
@codspeed

codspeed Bot commented Oct 8, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 24.51%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

❌ 1 regressed benchmark
✅ 580 untouched benchmarks
⏩ 32 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ test_feature[shadow-slot-comments] 83.3 µs 110.4 µs -24.51%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing gaborbernat:fix/alloc-selector-compile (29f7ef8) with main (74c1b54)

Open in CodSpeed

Footnotes

  1. 32 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

A failed buffer growth in the selector parser set the same flag as invalid
syntax. select(), select_one(), css_to_xpath() and the rewriter then reported
the allocation failure as SelectorSyntaxError, and a forgiving :is() or
:where() list dropped the arm and matched with the rest. A failed build of the
document's element index fell back to a full walk and returned an answer as if
nothing had failed.

The parser now records an allocation failure with no reason, which sel_raise
turns into MemoryError and a forgiving list stops on. select(), select_one()
and find_all() build the index before their walk and raise MemoryError when
the build fails. The check sits outside the walking functions because a
failure branch inside them stopped GCC's LTO from inlining the matcher into
the loop, which cost select 3,000 instructions a call under callgrind.

The regression tests sit in tests/fuzz_build next to the operation-limit
checks, because only the fuzz build defines the allocation-failure hook;
fuzz-smoke runs them under ASan and the normal matrix skips them.
@gaborbernat
gaborbernat force-pushed the fix/alloc-selector-compile branch from 29f7ef8 to 8a0ce6d Compare October 8, 2026 15:43
@gaborbernat
gaborbernat merged commit d3c6243 into tox-dev:main Oct 8, 2026
7 of 61 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant