
Vulnerabilidade de escrita fora dos limites encontrada na pesquisa de texto completo (tsvector/tsquery) do PostgreSQL CVE-2026-14662, registro da verificação executando lado a lado no Docker as versões antes da correção (18.4) e após a correção (18.6), e material de apresentação
Este é um registro de como investiguei o CVE-2026-14662, encontrado nos tipos de dados de busca de texto completo do PostgreSQL (tsvector / tsquery), executando simultaneamente as versões corrigida (18.4) e não corrigida (18.6) em Docker e observando de fato a diferença de comportamento.
Foi compilado para fins de aprendizado em segurança e inclui também os materiais da apresentação.
| Item | Conteúdo |
|---|
| CVE | CVE-2026-14662 |
| Alvo | tsvector / tsquery do PostgreSQL |
| Tipo | Tamanho de alocação insuficiente e escrita fora dos limites devido a estouro de inteiro (integer wraparound) |
| CVSS | 8.8 / 10.0 (Alto) |
| Data de divulgação | 2026-08-13 |
| Versões corrigidas | 18.5, 17.11, 16.15, 15.19, 14.24 |
A cadeia de fraquezas tem a seguinte forma.
CWE-190 (Estouro de inteiro) → CWE-131 (Cálculo incorreto do tamanho do buffer) → CWE-787 (Escrita fora dos limites)
=Causa raiz =Impacto final
.
├── docs/
│ └── presentation.md Materiais da apresentação (explicação da vulnerabilidade + explicação das CWEs)
└── docker/
├── README.md Como usar o ambiente de reprodução
├── docker-compose.yml
├── init/ SQL de inicialização comum aplicado a ambas as versões
├── test/ PoC e SQL de demonstração
└── run.sh Inicialização → execução do PoC → exibição dos resultados
Se você tiver Docker e Docker Compose, basta o seguinte para reproduzir.
cd docker
./run.sh
Os contêineres das versões 18.4 e 18.6 serão iniciados, o mesmo SQL de inicialização será aplicado a ambos e, em seguida, o mesmo PoC será executado em ambos, exibindo os resultados lado a lado. Consulte docker/README.md para obter detalhes.
| Teste | PostgreSQL 18.4 (antes da correção) | PostgreSQL 18.6 (após a correção) |
|---|---|---|
| Repetir auto-OR de tsquery | Sucesso sem erros até 18 vezes (final: 8.650.748 bytes) | ERROR: tsquery is too large na 17ª vez |
| Matriz de palavras com comprimento total de 1.200.000 bytes | Conclui com sucesso | ERROR: string is too long for tsvector (1200000 bytes, max 1048575 bytes) |
| Uma única palavra de 3.000 bytes | Conclui com sucesso | ERROR: word is too long (3000 bytes, max 2046 bytes) |
Antes da correção, dados que excedem o limite da representação interna (MAXSTRPOS = 1.048.575, derivado de um campo de bits de 20 bits) são aceitos sem gerar nenhum erro.
Além disso, o motivo pelo qual a versão 18.6 para exatamente na "17ª vez" pode ser explicado por cálculo, e confirmamos que o valor teórico e o valor medido coincidem byte a byte (detalhes nos materiais da apresentação).
Para verificar as diferenças, usei um clone do repositório oficial do PostgreSQL, mas como o tamanho é grande (185MB cada), não os incluí neste repositório. Se necessário, obtenha-os da seguinte forma.
git clone --branch REL_18_4 --depth 1 https://github.com/postgres/postgres.git postgres-18.4
git clone --branch REL_18_6 --depth 1 https://github.com/postgres/postgres.git postgres-18.6
Os arquivos que receberam a correção são os três seguintes.
src/backend/utils/adt/tsquery_util.csrc/backend/utils/adt/tsvector.csrc/backend/utils/adt/tsvector_op.c