highCVE-2026-53530Jul 7, 2026

CVE-2026-53530: ratex-parser Process Abort via UTF-8 Multibyte Delimiter in \verb

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Sending a nine-byte LaTeX string with a non-ASCII delimiter to ratex-parser causes the Rust process to abort immediately, taking down any server or pipeline that renders untrusted LaTeX.

Packageratex-parser
Ecosystemrust
Affected< 0.1.11
Fixed in0.1.11

The problem

The `parse_symbol_inner` function in `crates/ratex-parser/src/parser.rs` handles `\verb` by slicing its argument with raw byte indices (`arg[1..arg.len() - 1]`). When the delimiter character is a multibyte UTF-8 sequence, byte index 1 falls inside that character and Rust panics.

Because the release profile sets `panic = "abort"` in `Cargo.toml`, the panic does not unwind or stay contained to a thread. It aborts the entire process, so one malicious formula is enough to cause a full-service denial of service in any web endpoint, WASM renderer, or FFI-embedded build.

Proof of concept

A working proof-of-concept for CVE-2026-53530 in ratex-parser, with the exact payload below.

bash
$ printf '\\verb\xc3\xa9x\xc3\xa9\n' | ./target/release/parse
thread 'main' panicked at crates/ratex-parser/src/parser.rs:910:27:
start byte index 1 is not a char boundary; it is inside 'é' (bytes 0..2 of string)
Aborted (core dumped)

The input `\verbéxé` strips the `\verb` prefix, leaving `arg = "éxé"`. The character `é` (U+00E9) encodes as two bytes (`0xC3 0xA9`), so `arg.len()` is 5. The byte-length guard `arg.len() < 2` passes, and `arg[1..4]` attempts a slice starting at byte 1, which is the interior of a two-byte sequence.

Rust enforces UTF-8 char boundaries on string slices and panics unconditionally here.

The lexer (`lexer.rs`, `lex_verb`) already tokenises the verb argument correctly using char-aware iteration. Only the parser then re-slices the already-tokenised string with byte offsets, which is the isolated root cause (CWE-129, improper index validation; CWE-248, uncaught exception; CWE-400, uncontrolled resource consumption via abort).

The fix replaces byte slicing with char-collection: `let chars: Vec<char> = arg.chars().collect()`, guards on `chars.len() < 2`, and reconstructs the body as `chars[1..chars.len()-1].iter().collect::<String>()`. The same char-aware strip replaces the `*`-prefix byte slice at line 905.

The fix

Upgrade `ratex-parser` to **0.1.11**. The patch rewrites the `\verb` body extraction in `parse_symbol_inner` to use character indices instead of byte indices, matching the UTF-8-correct approach already used in the lexer. If you embed RaTeX in a long-running service before you can upgrade, wrap the call site in `std::panic::catch_unwind` as a short-term mitigation, and consider whether `panic = "abort"` is appropriate for your deployment profile.

Reporter not attributed.

References: [1][2]

Related research