Every CVE Search/ Get API key

CVE-2026-67550

re2: Out-of-bounds heap read in `exec`/`test`/`match` via attacker-influenced `lastIndex` on a non-ASCII subject → uncatchable process crash (DoS)

5.7 MediumFix available

re2 provides Node.js bindings for Google's RE2 regular expression engine. Prior to 1.25.2, re2 validates lastIndex against the UTF-8 byte length of a subject but uses it as a UTF-16 code-unit offset in exec, test, match, replace, and split, allowing an attacker-influenced lastIndex on a non-ASCII subject to trigger an out-of-bounds heap read and an uncatchable process crash, with limited heap information disclosure in some cases. This issue is fixed in 1.25.2.

Attack profile

How easy it is to exploit

Attack vector
Local
Complexity
High
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
Low
Integrity
None
Availability
High

Exploit likelihood

0.16%Unchanged since Jul 31, 2026
90%50%10%1%

What each source reports

CVE List Score used 5.7

re2: Out-of-bounds heap read in `exec`/`test`/`match` via attacker-influenced `lastIndex` on a non-ASCII subject → uncatchable process crash (DoS)

re2 provides Node.js bindings for Google's RE2 regular expression engine. Prior to 1.25.2, re2 validates lastIndex against the UTF-8 byte length of a subject but uses it as a UTF-16 code-unit offset in exec, test, match, replace, and split, allowing an attacker-influenced lastIndex on a non-ASCII subject to trigger an out-of-bounds heap read and an uncatchable process crash, with limited heap information disclosure in some cases. This issue is fixed in 1.25.2.

Record
CVE-2026-67550
CVSS 3.1
5.7 MediumCVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:H
Weakness
CWE-125
Updated
2026-11-02
EUVDEUVD-2026-51297 5.7

re2 provides Node.js bindings for Google's RE2 regular expression engine. Prior to 1.25.2, re2 validates lastIndex against the UTF-8 byte length of a subject but uses it as a UTF-16 code-unit offset in exec, test, match, replace, and split, allowing an attacker-influenced lastIndex on a non-ASCII subject to trigger an out-of-bounds heap read and an uncatchable process crash, with limited heap information disclosure in some cases. This issue is fixed in 1.25.2.

Record
EUVD-2026-51297
CVSS 3.1
5.7 MediumCVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-ff84-5f28-78qj 5.7

re2: Out-of-bounds heap read in `exec`/`test`/`match` via attacker-influenced `lastIndex` on a non-ASCII subject → uncatchable process crash (DoS)

Summary

re2 validates the user-settable lastIndex against the subject's UTF-8 byte length but then uses it as a UTF-16 code-unit count to walk the subject buffer, with no bounds check. For any non-ASCII subject, the byte length is larger than the true character count, so a lastIndex between those two values passes validation while pointing past the end of the buffer. The subsequent walk reads out of bounds. With a large subject the read marches into unmapped memory and the process dies with SIGABRT/SIGSEGV — an uncatchable crash (try/catch cannot stop it), i.e. a denial of service for any worker/process that runs the match. In some cases the out-of-bounds bytes are copied into the returned value (a bounded, best-effort heap information leak).

Root cause

The subject wrapper stores the UTF-8 byte length in StrVal::length:

  • lib/addon.cc:200 auto argLength = utf8Length(s, isolate); — UTF-8 byte count
  • lib/addon.cc:209 lastStringValue.reset(buffer, argSize, argLength, startFrom, false, isAscii);

setIndex then validates the (UTF-16) lastIndex against that byte length and walks the buffer by character count:

// lib/addon.cc:229
void StrVal::setIndex(size_t newIndex) {
    isValidIndex = newIndex <= length;   // length == UTF-8 BYTE length, not UTF-16 length
    if (!isValidIndex) { index = newIndex; byteIndex = 0; return; }
    ...
    // addon.cc:263
    byteIndex = index < newIndex
        ? getUtf16PositionByCounter(data, byteIndex, newIndex - index)
        : getUtf16PositionByCounter(data, 0, newIndex);
    index = newIndex;
}

getUtf16PositionByCounter reads data[from] and advances by the UTF-8 char size with no check of from against the buffer size:

// lib/wrapped_re2.h:264
inline size_t getUtf16PositionByCounter(const char *data, size_t from, size_t n) {
    for (; n > 0; --n) {
        size_t s = getUtf8CharSize(data[from]);   // <-- OOB read once `from` passes the buffer end
        from += s;
        if (s == 4 && n >= 2) --n;
    }
    return from;
}

lastIndex is user-settable to any positive integer (capped only at >= 0, no upper bound):

// lib/accessors.cc:166
NAN_SETTER(WrappedRE2::SetLastIndex) {
    ...
    int n = value->NumberValue(...).FromMaybe(0);
    re2->lastIndex = n <= 0 ? 0 : n;   // no upper bound relative to the subject
}

For an ASCII subject the byte length equals the UTF-16 length, so the guard is correct — this only triggers on non-ASCII subjects. The out-of-bounds read happens inside prepareArgument for any global/sticky regex, reached by exec, test, String.prototype.match, replace, and split.

Proof of concept

Minimal (AddressSanitizer, deterministic OOB read):

const RE2 = require('re2');
const re = new RE2('a', 'y');   // sticky; 'g' also works
re.lastIndex = 3;               // 3 <= byteLen(4) passes the guard; only 2 real chars exist
re.exec('éé');                  // U+00E9 = 2 bytes each

Built with -fsanitize=address, this aborts with:

ERROR: AddressSanitizer: heap-buffer-overflow ... READ of size 1
  #0 getUtf16PositionByCounter   wrapped_re2.h:268
  #1 StrVal::reset               addon.cc:277
  #2 WrappedRE2::prepareArgument addon.cc:209
  #3 WrappedRE2::Exec            exec.cc:17

The overflowed region is the subject buffer allocated by node::Buffer::New at addon.cc:205.

Real-world impact on the shipped prebuilt binary (no ASAN) — uncatchable crash:

const RE2 = require('re2');
const s = '中'.repeat(40000000);        // UTF-16 length 40M, UTF-8 bytes 120M
const re = new RE2('a', 'y');
re.lastIndex = Buffer.byteLength(s) - 1; // passes the byte-length guard, far exceeds real char count
re.exec(s);                              // walks into unmapped memory -> SIGSEGV (exit 139)

try { ... } catch (e) {} around the call does not prevent termination — it is a native fault, not a JS exception. Validated on a…

Record
GHSA-ff84-5f28-78qj
CVSS 3.1
5.7 MediumCVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:H
Weakness
CWE-125
Updated
2026-11-02
NVD 5.7

re2 provides Node.js bindings for Google's RE2 regular expression engine. Prior to 1.25.2, re2 validates lastIndex against the UTF-8 byte length of a subject but uses it as a UTF-16 code-unit offset in exec, test, match, replace, and split, allowing an attacker-influenced lastIndex on a non-ASCII subject to trigger an out-of-bounds heap read and an uncatchable process crash, with limited heap information disclosure in some cases. This issue is fixed in 1.25.2.

Record
CVE-2026-67550
CVSS 3.1
5.7 MediumCVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:H
Weakness
CWE-125
Updated
2026-11-02
OSV 5.7

re2: Out-of-bounds heap read in `exec`/`test`/`match` via attacker-influenced `lastIndex` on a non-ASCII subject → uncatchable process crash (DoS)

re2 provides Node.js bindings for Google's RE2 regular expression engine. Prior to 1.25.2, re2 validates lastIndex against the UTF-8 byte length of a subject but uses it as a UTF-16 code-unit offset in exec, test, match, replace, and split, allowing an attacker-influenced lastIndex on a non-ASCII subject to trigger an out-of-bounds heap read and an uncatchable process crash, with limited heap information disclosure in some cases. This issue is fixed in 1.25.2.

Record
CVE-2026-67550
CVSS 3.1
5.7 MediumCVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:H
Weakness
CWE-125
Updated
2026-11-02
OSVDEBIAN-CVE-2026-67550 5.7

re2 provides Node.js bindings for Google's RE2 regular expression engine. Prior to 1.25.2, re2 validates lastIndex against the UTF-8 byte length of a subject but uses it as a UTF-16 code-unit offset in exec, test, match, replace, and split, allowing an attacker-influenced lastIndex on a non-ASCII subject to trigger an out-of-bounds heap read and an uncatchable process crash, with limited heap information disclosure in some cases. This issue is fixed in 1.25.2.

Record
DEBIAN-CVE-2026-67550
CVSS 3.1
5.7 MediumCVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:H
Updated
2026-11-02
OSVGHSA-ff84-5f28-78qj 5.7

re2: Out-of-bounds heap read in `exec`/`test`/`match` via attacker-influenced `lastIndex` on a non-ASCII subject → uncatchable process crash (DoS)

Summary

re2 validates the user-settable lastIndex against the subject's UTF-8 byte length but then uses it as a UTF-16 code-unit count to walk the subject buffer, with no bounds check. For any non-ASCII subject, the byte length is larger than the true character count, so a lastIndex between those two values passes validation while pointing past the end of the buffer. The subsequent walk reads out of bounds. With a large subject the read marches into unmapped memory and the process dies with SIGABRT/SIGSEGV — an uncatchable crash (try/catch cannot stop it), i.e. a denial of service for any worker/process that runs the match. In some cases the out-of-bounds bytes are copied into the returned value (a bounded, best-effort heap information leak).

Root cause

The subject wrapper stores the UTF-8 byte length in StrVal::length:

  • lib/addon.cc:200 auto argLength = utf8Length(s, isolate); — UTF-8 byte count
  • lib/addon.cc:209 lastStringValue.reset(buffer, argSize, argLength, startFrom, false, isAscii);

setIndex then validates the (UTF-16) lastIndex against that byte length and walks the buffer by character count:

// lib/addon.cc:229
void StrVal::setIndex(size_t newIndex) {
    isValidIndex = newIndex <= length;   // length == UTF-8 BYTE length, not UTF-16 length
    if (!isValidIndex) { index = newIndex; byteIndex = 0; return; }
    ...
    // addon.cc:263
    byteIndex = index < newIndex
        ? getUtf16PositionByCounter(data, byteIndex, newIndex - index)
        : getUtf16PositionByCounter(data, 0, newIndex);
    index = newIndex;
}

getUtf16PositionByCounter reads data[from] and advances by the UTF-8 char size with no check of from against the buffer size:

// lib/wrapped_re2.h:264
inline size_t getUtf16PositionByCounter(const char *data, size_t from, size_t n) {
    for (; n > 0; --n) {
        size_t s = getUtf8CharSize(data[from]);   // <-- OOB read once `from` passes the buffer end
        from += s;
        if (s == 4 && n >= 2) --n;
    }
    return from;
}

lastIndex is user-settable to any positive integer (capped only at >= 0, no upper bound):

// lib/accessors.cc:166
NAN_SETTER(WrappedRE2::SetLastIndex) {
    ...
    int n = value->NumberValue(...).FromMaybe(0);
    re2->lastIndex = n <= 0 ? 0 : n;   // no upper bound relative to the subject
}

For an ASCII subject the byte length equals the UTF-16 length, so the guard is correct — this only triggers on non-ASCII subjects. The out-of-bounds read happens inside prepareArgument for any global/sticky regex, reached by exec, test, String.prototype.match, replace, and split.

Proof of concept

Minimal (AddressSanitizer, deterministic OOB read):

const RE2 = require('re2');
const re = new RE2('a', 'y');   // sticky; 'g' also works
re.lastIndex = 3;               // 3 <= byteLen(4) passes the guard; only 2 real chars exist
re.exec('éé');                  // U+00E9 = 2 bytes each

Built with -fsanitize=address, this aborts with:

ERROR: AddressSanitizer: heap-buffer-overflow ... READ of size 1
  #0 getUtf16PositionByCounter   wrapped_re2.h:268
  #1 StrVal::reset               addon.cc:277
  #2 WrappedRE2::prepareArgument addon.cc:209
  #3 WrappedRE2::Exec            exec.cc:17

The overflowed region is the subject buffer allocated by node::Buffer::New at addon.cc:205.

Real-world impact on the shipped prebuilt binary (no ASAN) — uncatchable crash:

const RE2 = require('re2');
const s = '中'.repeat(40000000);        // UTF-16 length 40M, UTF-8 bytes 120M
const re = new RE2('a', 'y');
re.lastIndex = Buffer.byteLength(s) - 1; // passes the byte-length guard, far exceeds real char count
re.exec(s);                              // walks into unmapped memory -> SIGSEGV (exit 139)

try { ... } catch (e) {} around the call does not prevent termination — it is a native fault, not a JS exception. Validated on a…

Record
GHSA-ff84-5f28-78qj
CVSS 3.1
5.7 MediumCVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:H
Weakness
CWE-125
Updated
2026-11-02
OSVUBUNTU-CVE-2026-67550 5.7

re2 provides Node.js bindings for Google's RE2 regular expression engine. Prior to 1.25.2, re2 validates lastIndex against the UTF-8 byte length of a subject but uses it as a UTF-16 code-unit offset in exec, test, match, replace, and split, allowing an attacker-influenced lastIndex on a non-ASCII subject to trigger an out-of-bounds heap read and an uncatchable process crash, with limited heap information disclosure in some cases. This issue is fixed in 1.25.2.

Record
UBUNTU-CVE-2026-67550
CVSS 3.1
5.7 MediumCVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:H
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
re2
npm
through 1.25.1
1.25.2
node-re2
Debian:12
All versions, plus listed versions
1.18.0+~cs2.13.8-11.19.2+~cs2.13.13-11.20.10+~cs2.13.13-11.20.12+~cs2.13.13-11.21.4+~cs2.13.13-1
Show 8 moreShow less
1.21.4+~cs2.13.13-21.22.1+~cs1.3.5-11.23.3+~cs1.3.5-11.25.0+~cs1.6.0-11.25.0+~cs1.6.0-21.26.1+~cs1.7.0-11.26.1+~cs1.8.0-11.27.0+~cs1.8.1-1
None known
node-re2
Debian:13
All versions, plus listed versions
1.21.4+~cs2.13.13-21.22.1+~cs1.3.5-11.23.3+~cs1.3.5-11.25.0+~cs1.6.0-11.25.0+~cs1.6.0-21.26.1+~cs1.7.0-11.26.1+~cs1.8.0-11.27.0+~cs1.8.1-1
None known
node-re2
Debian:14
before 1.26.1+~cs1.7.0-1, plus listed versions
1.21.4+~cs2.13.13-21.22.1+~cs1.3.5-11.23.3+~cs1.3.5-11.25.0+~cs1.6.0-11.25.0+~cs1.6.0-2
1.26.1+~cs1.7.0-1
re2
npm
before 1.25.2
1.25.2
node-re2
Ubuntu:20.04:LTS
All versions, plus listed versions
1.10.4-11.10.5-11.10.5-1build1
None known
node-re2
Ubuntu:22.04:LTS
All versions, plus listed versions
1.15.9+~cs1.8.18-11.15.9+~cs1.8.18-21.16.0+~cs2.12.8-21.17.1+~cs2.12.8-11.17.2+~cs2.13.8-11.17.3+~cs2.13.8-1
None known
node-re2
Ubuntu:24.04:LTS
All versions, plus listed versions
1.18.0+~cs2.13.8-1build11.19.2+~cs2.13.13-1build11.20.10+~cs2.13.13-1
None known
node-re2
Ubuntu:26.10
All versions, plus listed versions
1.23.3+~cs1.3.5-11.25.0+~cs1.6.0-21.26.1+~cs1.7.0-1
None known
node-re2
Ubuntu:26.04:LTS
All versions, plus listed versions
1.22.1+~cs1.3.5-11.22.1+~cs1.3.5-1build21.22.1+~cs1.3.5-1build31.23.3+~cs1.3.5-1
None known

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-67550 is a medium vulnerability (CVSS 5.7) in Uhop Node-re2, published July 30, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.16%, the 5th percentile. Fixed in re2 1.25.2.

References

Show 4 moreShow less
This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open