Skip to content

OOM: Astro JSX parser has no depth limit on malformed input (<D} causes unbounded allocation) #4

@jasikpark

Description

@jasikpark

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

<D}

(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_containerparse_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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions