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