Fix out-of-bounds read in JS highlighter on trailing backslash in `${}` (#31435)
Fixes #31434
## Repro
Launching the REPL and typing `` `${\ `` verbatim (backtick, `$`, `{`,
trailing backslash) dumps garbage from adjacent memory and segfaults on
1.3.14, and panics on 1.4.0-canary:
```
panic: range end index 5 out of range for slice of length 4
```
Node and Deno handle the same input without crashing.
## Cause
The `QuickAndDirtyJavaScriptSyntaxHighlighter` (`src/bun_core/fmt.rs`) —
used by the REPL to highlight the current line — scans the contents of a
`${...}` interpolation with this loop:
```rust
while i < text.len() && text[i] != b'}' {
if text[i] == b'\\' {
i += 1; // when '\' is the LAST byte, i becomes text.len()
}
i += 1; // ...then this bumps i to text.len() + 1
}
```
When the backslash is the final byte, the skip runs the cursor past the
end, and the subsequent slice `&text[curly_start + 2..i]` (fmt.rs:1970)
is out of range. For `` `${\ `` the bytes are `['`', '$', '{', '\\']`
(len 4) and `i` ends at 5.
The old Zig build had no bounds check and read adjacent memory (the
"garbage text" in the report) before segfaulting; the Rust port turns
the over-read into a panic. Same root cause, both behaviors.
The sibling string-scan loop a few lines below already guards its
backslash skip with `i + 1 < text.len()` — the `${...}` loop was missing
that guard.
## Fix
Add the same `i + 1 < text.len()` guard to the `${...}` inner loop so
the backslash skip can't push the cursor past the end. When the
backslash is the last byte it's no longer skipped, the loop exits with
`i == text.len()`, and the slice stays in range. All other inputs
(backslash followed by more content, `\}`, `\\`) are unchanged. Mirrored
into the `.zig` porting reference.
## Verification
Regression cases added to `test/js/bun/util/highlighter.test.ts`,
exercising the highlighter via `highlightJavaScript` from
`bun:internal-for-testing` (same options the REPL uses).
- `USE_SYSTEM_BUN=1 bun test test/js/bun/util/highlighter.test.ts` →
crashes (exit 132, `range end index 5 out of range for slice of length
4` — matches the issue's crash report).
- `bun bd test test/js/bun/util/highlighter.test.ts` → 5 pass, 0 fail. R
robobun committed
99f4679dbbfd563ab286d2d35be8d34825393966
Parent: f958cbd
Committed by GitHub <noreply@github.com>
on 5/26/2026, 9:25:14 PM