Summary
The oxc Astro parser allocates unboundedly when given malformed nested JSX. The 3-byte input <D} causes an OOM kill. No recursion depth limit or allocation guard exists in the Astro JSX parser paths.
Reproducer
(3 bytes, valid UTF-8)
Reproduce with cargo-fuzz (fuzz target and artifact available in this PR for compiler-rs, an Astro component compiler built on this fork of oxc): withastro/compiler-rs#5
cargo +nightly fuzz run transform_no_panic \
fuzz/artifacts/transform_no_panic/minimized-from-31fa1c120335eb994219313c5f26a18a09cb6fc6
Or directly:
use oxc_allocator::Allocator;
use oxc_parser::{ParseOptions, Parser};
use oxc_span::SourceType;
let allocator = Allocator::default();
let src = "<D}";
let ret = Parser::new(&allocator, src, SourceType::astro())
.with_options(ParseOptions::default())
.parse_astro();
// OOM before this line is reached
Observed behaviour
The process is killed by the OOM killer (or panics with an allocation failure) before parse_astro() returns. RSS climbs until the 4 GiB libFuzzer limit is hit.
SUMMARY: libFuzzer: out-of-memory
The allocation loop is in parse_astro_jsx_expression_container → parse_astro_jsx_element.
Expected behaviour
The parser should return a parse error / diagnostic, not exhaust memory.
Root cause
The Astro JSX parser recurses into parse_astro_jsx_element without tracking depth. A < that opens an element followed by content that keeps the parser in an expression-container loop causes unbounded allocation on the arena allocator.
Fix suggestion
Add a recursion depth counter (analogous to what the regular oxc JSX parser does for HTML nesting) and return a diagnostic error when the limit is exceeded.
Environment
withastro/oxc branch feat/astro (as of 2026-02-17)
- Rust nightly (cargo-fuzz requirement)
- Reproducible on macOS arm64 and Linux x86-64
Note: this issue was drafted with AI assistance (Claude), which also helped build the fuzzing setup, artifact minimization, and root cause analysis. I'm responsible for the content and have verified the reproducer.
Summary
The oxc Astro parser allocates unboundedly when given malformed nested JSX. The 3-byte input
<D}causes an OOM kill. No recursion depth limit or allocation guard exists in the Astro JSX parser paths.Reproducer
(3 bytes, valid UTF-8)
Reproduce with cargo-fuzz (fuzz target and artifact available in this PR for
compiler-rs, an Astro component compiler built on this fork of oxc): withastro/compiler-rs#5cargo +nightly fuzz run transform_no_panic \ fuzz/artifacts/transform_no_panic/minimized-from-31fa1c120335eb994219313c5f26a18a09cb6fc6Or directly:
Observed behaviour
The process is killed by the OOM killer (or panics with an allocation failure) before
parse_astro()returns. RSS climbs until the 4 GiB libFuzzer limit is hit.The allocation loop is in
parse_astro_jsx_expression_container→parse_astro_jsx_element.Expected behaviour
The parser should return a parse error / diagnostic, not exhaust memory.
Root cause
The Astro JSX parser recurses into
parse_astro_jsx_elementwithout tracking depth. A<that opens an element followed by content that keeps the parser in an expression-container loop causes unbounded allocation on the arena allocator.Fix suggestion
Add a recursion depth counter (analogous to what the regular oxc JSX parser does for HTML nesting) and return a diagnostic error when the limit is exceeded.
Environment
withastro/oxcbranchfeat/astro(as of 2026-02-17)Note: this issue was drafted with AI assistance (Claude), which also helped build the fuzzing setup, artifact minimization, and root cause analysis. I'm responsible for the content and have verified the reproducer.