SIGN IN SIGN UP
oven-sh / bun UNCLAIMED

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one

0 0 150 Rust

js_parser: guard stack depth when parsing nested JSX elements (#31537)

## Summary

The TSX parser SIGSEGVs on deeply-nested JSX. Found by fuzzing: a source
like `() => <div>` repeated thousands of times crashes with a bare
`SIGSEGV` (exit 139) and no crash-handler output — it dies on the stack
guard page.

## Repro

```js
// ~50k repetitions of "() => <div>"
const src = "() => <div>".repeat(50_000);
new Bun.Transpiler({ loader: "tsx", target: "bun", minifyWhitespace: true, deadCodeElimination: true }).transformSync(src);
// -> SIGSEGV (exit 139)
```

## Cause

`parse_jsx_element` (`src/js_parser/parse/parse_jsx.rs`) recurses
**directly** for every nested child element (`<a><b><c>...`) at the
`T::TLessThan` child branch. Every other recursive parse entry point
(`parse_expr_common`, `parse_stmt`, `parse_property`, `parse_binding`,
the TypeScript skippers) consults the parser's `stack_check`, but this
JSX child-recursion path never did.

The fuzzer input `() => <div>() => <div>...` nests that many `<div>`
children — the `() =>` between each pair is lexed as JSX text
(`TStringLiteral`) — so the recursion depth grows unbounded and runs off
the end of the stack.

## Fix

Add the standard guard at the top of `parse_jsx_element`:

```rust
if !p.stack_check.is_safe_to_recurse() {
    return Err(err!("StackOverflow"));
}
```

The parse entry points (`parse_entry.rs`) already catch
`err!("StackOverflow")` and turn it into a graceful `Maximum call stack
size exceeded` diagnostic, so the transpiler now reports a catchable
`SyntaxError` instead of crashing. Valid nested JSX is unaffected — the
guard only fires near stack exhaustion, well past any realistic nesting
depth.

## Verification

- `test/bundler/transpiler/jsx-deep-nesting-stack-overflow.test.ts`:
spawns a child that transpiles 50k-deep `() => <div>` and asserts it is
**not** killed by a signal and reports the call-stack error.
  - **Without the fix**: child exits with `SIGSEGV` → test fails.
- **With the fix**: graceful `Maximum call stack size exceeded` → test
passes.
- Existing JSX/transpiler suites still pass (`transpiler.test.js`: 166
pass / 0 fail; `scope-mismatch-panic.test.ts`: all pass).
- Spot-checked that valid nested JSX (`<a><b><c>hi</c></b></a>`) still
transpiles correctly.
R
robobun committed
ecfdaa66f3869f8978dc822284ca1ba0e1722ebb
Parent: 4845d4f
Committed by GitHub <noreply@github.com> on 5/29/2026, 1:12:09 AM