` re 2 ` valida a tabela de usuário 'lastIndex' contra **UTF- do sujeito 8 comprimento de byte** mas então usa- o como um **UTF- 16 contagem de unidades de código** para caminhar pelo buffer do assunto, sem limites verificados. Para qualquer assunto não- ASCII, o comprimento do byte é maior que a contagem de caracteres verdadeira, por isso um `lastIndex` entre esses dois valores passa validação enquanto aponta para além do fim do buffer. A caminhada subsequente lê fora dos limites. Com um assunto grande, a leitura marcha para a memória não mapeada e o processo morre com **SIGABRT/SIGSEGV** — um crash incaptável (`try/catcha' não pode pará-lo), ou seja, uma negação de serviço para qualquer trabalhador/ processo que executa a partida. Em alguns casos, os bytes fora de limites são copiados no valor devolvido (um vazamento de informação de pilha limitado, com melhor esforço). O envoltório do assunto armazena o UTF- 8 **byte** comprimento em `StrVal:: comprimento`: - `lib/ addon.cc: 200 ` ` auto- argLongth = utf 8 Duração( s, isolado); ' — UTF- 8 **byte** contagem - `lib/ addon.cc: 209 ` ` lastStringValue. reset( buffer, argSize, argLongth, startFrom, false, isAscii);`.
`setIndex`, em seguida, valida o (UTF- 16 ) `lastIndex` contra esse comprimento de byte e caminha o buffer por contagem de caracteres: ``` cpp // lib/ addon.cc: 229 StrVal::setIndex(tamanho_ t newIndex) { isValidIndex = newIndex <= comprimento; // comprimento == UTF- 8 Comprimento BYTE, não UTF- 16 comprimento se (! éValidIndex) { índice = novoIndex; byteIndex = 0; retorno; }... // addon.cc: 263 byteIndex = índice < novoIndex? getUtf 16 PosiçãoPorContedor(dados, byteIndex, novoIndex - índice): getUtf 16 PosiçãoPorConter( dados, 0, novo Índice); índice = novo Índice; } ``` `getUtf 16 PositionByCounter` lê `dados[de]` e avanços do UTF- 8 tamanho do char com ** sem verificação de `from` contra o tamanho do buffer**:.
``` cpp // lib/ wrapped_ re 2.h: 264 tamanho_t getUtf 16 PositionByCounter(const char *data, size_t from, size_t n) { for (; n > 0; --n) { tamanho_t s = getUtf 8 CharSize( data[from]); // = 2 ) -- n; } retorna de; } ``` `lastIndex` é uma tabela de usuário para qualquer inteiro positivo (capelado apenas em `>= 0 `, sem limite superior ': ``` cpp // lib/ accessores.cc: 166 NAN_SETTER( WrappedredRE 2::SetLastIndex) {... int n = valor->NumberValue(...). 0 ); re 2 ->último Índice = n <= 0? 0: n; // nenhum limite superior relativo ao sujeito } ```.
Para um sujeito ASCII, o comprimento de byte é igual ao UTF- 16 comprimento, então o guarda está correto — isto só ativa em assuntos não- ASCII. A leitura fora de limites acontece dentro de `prepareArgument` para qualquer "regex global`/ `adrequinte`, alcançada por `exec`, `teste`, `String.prototype.match`, `replace' e `split'. **Minimal (Sanitante de Endereço, leitura de OOB determinística):**. ```js const RE 2 = require('re 2 '); const re = novo RE 2 ('a', 'y'); // adhesivo; 'g' também funciona re.lastIndex = 3; // 3 <= byteLen( 4 ) passa o guarda; somente 2 Cartões reais existem re.exec( 'éé'); // U+ 00 E 9 = 2 bytes cada ``` Construído com `- fsanitize= address`, este aborta com. ``` ERROR: Endereço Sanitente: montão-buffer-overflow... LER do tamanho 1 # 0 getUtf 16 PosiçãoPorConter embrulhado_re 2.h: 268 # 1 StrVal:: reset addon.cc: 277 # 2 Embrulhado 2::prepareArgument addon.cc: 209 # 3 Embrulhado 2:: Execut executiv.cc: 17 ```.
A região transbordada é o buffer de assunto alocado por ` node::Buffer::New` no ` addon.cc: 205 `. ** Impacto real no mundo no binário pré-construído enviado (sem ASAN) — crash incaptável:** ```js const RE 2 = require('re 2 '); const s = '中'.repeat( 40000000 ); // UTF- 16 comprimento 40 M, UTF- 8 bytes 120 M const re = novo RE 2 ('a', 'y'); re.lastIndex = Buffer.byteLongueza(s) - 1; // passa pela guarda de comprimento de byte, muito superior à contagem de caracteres re.exec( s); // entra na memória não mapeada -> SIGSEGV (saída 139 ) ```.
` tentar {... } capturar (e) {}` em torno da chamada impede ** não** terminação — é uma falha nativa, não uma exceção JS. Validado em uma instalação limpa `npm re 2 @ 1.25.1 ` (último): stock pré- construído → SIGSEGV; ASAN build → o montão- buffer- overflow lido acima. - **Nivel de serviço (primário):** um crash nativo incaptável que termina o processo/trabalho do Nó. Alcançable remotamente e sem autenticação onde quer que um aplicativo (a) use um `global` ou `adega' `RE 2 `, (b) aplica- o a um sujeito não- ASCII, e (c) define `lastIndex` a partir de dados influenciados pelo atacante (por exemplo, retomando um scan/ pagination em um offset fornecido pelo cliente). - **Informação (secundária, melhor esforço):** os dados fora de limites `byteIndex` podem fazer com que os bytes adjacentes do pilhado sejam copiados no valor devolvido (por exemplo, o segmento principal de um resultado ` substituir`). Isto é limitado e não confiável — o buffer do assunto é `calloc`- atribuído (zero- preenchido) e a distância de sobre- leitura depende da interpretação de bytes fora de limites como UTF- 8 tamanhos — por isso é notado para completude, não como um primitivo confiável. Isto é distinto do GHSA- 8 hcv- x 26 h- mcgp (a amplificação global ` replace() ' abortar), que foi fixada em 1.25.1. Esta leitura fora de limites `lastIndex` é um defeito separado e permanece presente no 1.25.1.
Dois endurecimentos independentes; ou fecha o acidente, ambos são mais seguros. 1 **Amarre a caminhada** para que nunca possa ler além do buffer: ```` cpp inline size_ t getUtf 16 PositionByCounter(const char *data, size_t size, size_t from, size_t n) { for (; n > 0 & & from = 2 ) --n; } retorna de > tamanho? tamanho: de; } ``` (tamanho `tamanho ' através dos dois sites de chamadas em `StrVal::setIndex`). 2. **Validar o ` lastIndex` contra o verdadeiro UTF- 16 comprimento**, não o UTF- 8 comprimento do byte — por exemplo, armazenamento `s-> Longitude()` (UTF- 16 unidades) como o valor comparado em ` isValidIndex = newIndex `, por isso um `lastIndex` fora de alcance toma o caminho de retorno inicial existente `! isValidIndex`.
Corrigido em re 2 1.25.2. `lastIndex` agora é validado contra o UTF- do sujeito 16 comprimento em vez de seu UTF- 8 o comprimento do byte (`lib/ addon.cc`), por isso um 'lastIndex' fora de alcance é rejeitado antes do buffer ser caminhado. Como defesa em profundidade, a caminhada da unidade de código (`getUtf 16 PosiçãoByCounter` em `lib/ wrapped_re 2.h`) está agora limitado pelo tamanho do buffer e não pode mais ler além do fim. ** Remediação:** atualização para ` re 2 @ 1.25.2 ` ou mais tarde.
**Workaround** (se você não puder atualizar): não atribua `lastIndex` a partir de entradas não confiadas, nem o aperte para o comprimento da cadeia do sujeito (`str.length`) antes de chamar `exec`/`test`/`match`/`replace`/`split` em um assunto não- ASCII. Registro de aconselhamento: GHSA- ff 84 - 5 f 28 - 78 qj. Identificadores relacionados: CVE- 2026 - 67550. Tempo: GitHub Advisory Database publicou este registro em 2026 - 07 - 31 T 16: 53: 16.000 Z e lista a sua última modificação como 2026 - 07 - 31 T 16: 53: 16.000 Z.
Gravidade: MODERAR. Dados de pontuação publicados: CVSS_V 3: CVSS: 3.1 /AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:H. Software afetado e informações de versão: pacote npm re 2 — ECOSISTEM: introduzido 0, corrigido 1.25.2. Classificação e evidência: identificadores de fraqueza CWE- 125. O registro contém 5 suporte de referências nestes tipos: WEB, AVISO, EMBALAGAMENTO.