CSIRTS // UNIFIED SECURITY ADVISORY FEEDSYS ● ONLINE · POWERED BY INTELFUSIONS.COM

GHSA-j4r3-hg7j-8chg: node-re2: Out-of-bounds heap read in `replace`/`split` via a `Buffer` ending in a truncated multi-byte UTF-8 character → adjacent heap memory disclosed to JavaScript

mediumCVSS 5.1CVE-2026-71498
Summary re2 infers a character's byte length from its UTF-8 lead byte alone, with no bound on the bytes actually remaining in the input. Buffer arguments reach the native layer verbatim — only strings are re-encoded into well-formed UTF-8 — so a Buffer whose last byte is a multi-byte lead promises continuation bytes that are not there, and the result builders read up to 3 bytes past the end of the buffer. In replace() and split() those bytes are copied into the returned Buffer, disclosing adjacent heap memory to JavaScript. The trigger is deterministic and requires no special heap grooming. Only Buffer input is affected. String input was never at risk: re-encoding guarantees every multi-byte sequence is complete. Root cause getUtf8CharSize maps a lead byte to a length of 1–4 and never sees the input size: // lib/wrapped_re2.h inline size_t getUtf8CharSize(char ch) { return ((0xE5000000 >> ((ch >> 3) & 0x1E)) & 3) + 1; } Callers then read that many bytes. In the zero-width branch of replace(), the guard proves only that at least *one* byte remains: // lib/replace.cc else if ((size_t)offset < size) { auto sym_size = getUtf8CharSize(data[offset]); // may claim up to 4 bytes result.append(data + offset, sym_size); // reads data[offset .. offset + 3] byteIndex = offset + sym_size; } offset < size permits offset == size - 1, so a lead byte of 0xF0 makes append read data[size], data[size + 1] and data[size + 2]. Seven read sites shared the defect: | Site | Argument | Disclosed to JS | |---|---|---| | lib/replace.cc (zero-width branch) | subject | yes | | lib/replace.cc (callback replacer) | subject | yes | | lib/replace.cc (replacement scan) | replacement | yes | | lib/split.cc | subject | yes | | lib/pattern.cc translateRegExp (x2) | pattern | no | | lib/pattern.cc escapeRegExp | pattern | no | Three further callers were not vulnerable, because they use the result only to advance an index and never dereference past the end: getUtf16PositionByCounter in lib/wrap

Details

Source
GitHub Security Advisories (INTL · database · site)
Severity
medium — CVSS 5.1
Published
2026-08-06
Last updated
2026-08-06
Exploitation
Not in CISA KEV at last sync

Original advisory: https://github.com/advisories/GHSA-j4r3-hg7j-8chg

Referenced CVEs

CVECSIRTS overviewExternal
CVE-2026-71498coverage & exploitation statusNVD · CVE.org

Same CVEs, other sources

How other CERTs, PSIRTs and databases cover the vulnerabilities in this advisory.

More from GitHub Security Advisories