CVE
CVE-2026-6460, CVE-2026-6467, CVE-2026-14661, CVE-2026-16237, CVE-2026-16240, CVE-2026-18447, CVE-2026-116303, CVE-2026-116304, CVE-2026-116306, CVE-2026-116307, CVE-2026-116308, CVE-2026-116309, CVE-2026-116310, CVE-2026-116311, CVE-2026-116312, CVE-2026-116313, CVE-2026-116314, CVE-2026-116315, CVE-2026-116316, CVE-2026-116317, CVE-2026-116318, CVE-2026-116319, CVE-2026-116320, CVE-2026-116321, CVE-2026-116322, CVE-2026-116323, CVE-2026-116325, CVE-2026-116326, CVE-2026-116327, CVE-2026-116328, CVE-2026-116329, CVE-2026-116330, CVE-2026-116331, CVE-2026-116332, CVE-2026-116333, CVE-2026-116335, CVE-2026-116336, CVE-2026-120139, CVE-2026-120141, CVE-2026-120142, CVE-2026-120143, CVE-2026-120144, CVE-2026-120145, CVE-2026-120146, CVE-2026-120147, CVE-2026-120148, CVE-2026-120149, CVE-2026-120150, CVE-2026-120151, CVE-2026-120152, CVE-2026-120153, CVE-2026-120154
CWE
CWE-843, CWE-416, CWE-122, CWE-190, CWE-89, CWE-78, CWE-22, CWE-134, CWE-862, CWE-863
Affected Surface
- PostgreSQL server before 18.7, 17.12, 16.16, 15.20 and 14.25 (all supported branches), plus PostgreSQL 19 before RC 2
- PostgreSQL client tools from the same releases: `pg_dump` (`CVE-2026-116318`, `CVE-2026-116330`), `pg_restore` (`CVE-2026-116314`), `reindexdb` (`CVE-2026-116325`), `vacuumdb` (`CVE-2026-116313`) and `pg_basebackup` (`CVE-2026-16240`)
- PL/Perl (`plperl`), PL/Tcl (`pltcl`), PL/pgSQL, `dblink`, `postgres_fdw` and `dict_int` modules shipped with the server
- Server extensions compiled against pre-November headers (for example from PGDG RPM repositories), which load on the new server but do not carry the `CVE-2026-6460` checks until rebuilt
- PostgreSQL 14, which reaches end-of-life with 14.25 and will receive no further security fixes
TL;DR
PostgreSQL released 18.7, 17.12, 16.16, 15.20, 14.25 and 19 RC 2 on 12 November 2026. The update fixes 52 CVEs and more than 140 other bugs. By the CVSS scores in the announcement, 32 of the 52 are rated 8.8, three more are 8.0 or 8.1, three are 7.2, and the remaining 14 sit between 3.1 and 6.5. We found no reports of exploitation and no public exploit code when we checked on 12 November.
The bugs fall into three groups:
- Server-side code execution from an authenticated role. 29 of the CVE titles describe code execution. Most are type confusion, use-after-free, heap overflow or integer wraparound bugs that let a role that can create objects (or, for some, any database user) run code as the OS account running
postgres. The headline isCVE-2026-6460, a family of composite-type confusions reachable throughALTER ... SET DATA TYPEand similar DDL. Its fix adds a new “attribute versioning” safety layer and changes the extension ABI. - Client tools and replication trusting their input.
pg_dumpemitted child-table not-null constraint names unquoted, so a table owner could plant SQL that runs as whoever restores the dump (CVE-2026-116318).pg_restoretrusted archive contents and could be driven into out-of-bounds writes and path traversal (CVE-2026-116314). The tar-formatrestore.sqlalways used(null)as its\restrictkey, which undid the 2025 fix forCVE-2025-8714(CVE-2026-116330). A malicious logical replication publisher could name a system catalog and have a superuser-owned subscription write into it (CVE-2026-120145). - Privilege and policy bypasses. Row security and view
WHEREconditions could be sidestepped withINSERT ... ON CONFLICT(CVE-2026-16237,CVE-2026-120154),DROP OWNEDcould strip restrictive policies from tables the caller does not own (CVE-2026-116311), anddblink/ subscription connection strings acceptedsslkeylogfilefrom low-privileged users (CVE-2026-14661).
What to do now: Upgrade to 18.7, 17.12, 16.16 or 15.20, and upgrade every server extension in the same change. Upgrade the client binaries you use for dumps, restores and maintenance (pg_dump, pg_restore, reindexdb, vacuumdb) as well, including the copies baked into CI images and backup containers. After the upgrade, check the release notes for the REINDEX items and take a fresh full backup if you rely on incremental backups. If you are on PostgreSQL 14, this is the last release you will get.
Affected versions
| Component | CVEs (selection) | Affected | Fixed |
|---|---|---|---|
| PostgreSQL server 18.x | All 52 that apply to the branch | < 18.7 | 18.7 |
| PostgreSQL server 17.x | Most, including CVE-2026-6460 and CVE-2026-116325 | < 17.12 | 17.12 |
| PostgreSQL server 16.x / 15.x | Most, including CVE-2026-6460 and CVE-2026-116330 | < 16.16, < 15.20 | 16.16, 15.20 |
| PostgreSQL server 14.x (end-of-life) | Those that reach back to 14 | < 14.25 | 14.25 (final release) |
| PostgreSQL 19 pre-release | All | < 19 RC 2 | 19 RC 2 |
pg_dump | CVE-2026-116318 (18 only), CVE-2026-116330 (all branches) | Same as server | Same as server |
pg_restore | CVE-2026-116314 | Same as server | Same as server |
reindexdb / vacuumdb | CVE-2026-116325 (17 and 18), CVE-2026-116313 | Same as server | Same as server |
| Extensions built from PostgreSQL headers | CVE-2026-6460 (ABI change) | Builds against pre-November minors | Rebuilds against 18.7 / 17.12 / 16.16 / 15.20 / 14.25 |
Some CVEs only apply to newer branches. The fix commits carry Backpatch-through: trailers: for example CVE-2026-116318 is marked Backpatch-through: 18 and CVE-2026-116325 is Backpatch-through: 17, while CVE-2026-116330 and CVE-2026-116307 go back to 14.
Technical analysis
We read the fix commits on the REL_18_STABLE branch of the PostgreSQL GitHub mirror. Each security commit carries a Security: CVE-... trailer, which makes the mapping straightforward.
1. CVE-2026-6460 and the composite-type confusion family
PostgreSQL stores a composite (row-type) value as a heap tuple and decodes it with a tuple descriptor. If the descriptor does not match the shape the value was written with, the server reads fields at the wrong offsets with the wrong types. That is the root of CVE-2026-6460 and of most of the 8.8-rated bugs in this release: CVE-2026-116306 (field assignment), CVE-2026-120141 (materialized set-returning functions), CVE-2026-120142 (dropped columns), CVE-2026-120144 (type-cache invalidation), CVE-2026-120148 (extended statistics), CVE-2026-120151 (PL/pgSQL record fields) and others.
The upstream fix, commit 245eb832f, describes the approach:
Using a tuple descriptor that does not match the "shape" of a composite
datum can lead to crashes, spurious errors and can be used for exploits.
...
This commit introduces a new safety layer to prevent the worst of the
negative consequences. From now on, composite datums are marked as such
and named composite datums are versioned, allowing detection of
mismatches between a tuple descriptor and the composite during
deforming. In case of mismatches, errors are raised.
Around that core change, the 18.7 branch adds a long series of supporting commits: ALTER TABLE ... ALTER COLUMN TYPE is now refused by default when other objects depend on the column’s row type (1bb9942e20), row types used in foreign tables cannot change type (67d89bb220), and dependencies on types inside index expressions, defaults and constraint definitions are recorded and checked.
This is the part that matters for operations. The PGDG RPM packagers explained on Planet PostgreSQL that the fix turns DatumGetHeapTupleHeader() from a macro into a server function and makes heap_getattr() call a new heap_datum_check(). Because those headers are compiled into each extension:
- an old extension on a new server still loads, but it does not perform the new checks, so it may still be exploitable;
- a new extension on an old server fails to load with an
undefined symbolerror.
PGDG rebuilt every extension in its repositories against the new minors and asks users to upgrade the server and all extensions in the same transaction. If you build extensions yourself, or pull them from a vendor, rebuild them against the new headers.
2. pg_dump constraint names became SQL injection (CVE-2026-116318)
When a child table inherits a column and adds its own not-null constraint, pg_dump 18 prints that constraint inside the child’s CREATE TABLE. It quoted the column name with fmtId() but printed the constraint name raw (commit 9e4cde7927):
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -17213,13 +17213,11 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
- if (tbinfo->notnull_constrs[j][0] == '\0')
- appendPQExpBuffer(q, "NOT NULL %s",
- fmtId(tbinfo->attnames[j]));
- else
- appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
- tbinfo->notnull_constrs[j],
- fmtId(tbinfo->attnames[j]));
+ if (tbinfo->notnull_constrs[j][0] != '\0')
+ appendPQExpBuffer(q, "CONSTRAINT %s ",
+ fmtId(tbinfo->notnull_constrs[j]));
+ appendPQExpBuffer(q, "NOT NULL %s",
+ fmtId(tbinfo->attnames[j]));
The removed else branch passes tbinfo->notnull_constrs[j] straight into the output. A table owner who names a constraint something like x NOT NULL c); <arbitrary SQL>; -- gets that SQL written into the dump, and it runs with the privileges of whoever restores it, typically a superuser on a fresh cluster. The fix splits the call in two because fmtId() returns a pointer into a single shared buffer, so two fmtId() results cannot be used in one appendPQExpBuffer() call.
This matters most for platforms that dump tenant databases and restore them elsewhere: managed database migrations, staging refreshes and backup verification jobs.
3. pg_restore, tar-format restore.sql, reindexdb and vacuumdb
-
pg_restore(CVE-2026-116314, commit673ef2341e): The commit message is blunt: “pg_restore trusts the contents of the archives it reads, so a maliciously crafted archive can cause out-of-bounds reads and writes, or make pg_restore read files outside the archive directory.” Dependency IDs from the table of contents were used as array subscripts without validation, and some string handling read past the end of entries. Restoring a custom-format archive from an untrusted source could run code as the OS user runningpg_restore. -
Tar-format
restore.sql(CVE-2026-116330, commit17e57d06cd): The 2025 fix forCVE-2025-8714added\restrict/\unrestrictso a malicious server could not smuggle psql meta-commands into a dump. In the tar format’s undocumentedrestore.sqlmember, the restriction key was always the literal string(null). A compromised origin server could therefore end the restriction and run shell commands through psql on the client restoring the file. The fix generates a real key for--format=taras it already did for plain format. -
reindexdb --jobs --index(CVE-2026-116325, commitaac5e6f503): The parallel catalog query wrapped quoted identifiers inside a SQL string literal. An index name containing a single quote broke out of the literal:- "WHERE x.indexrelid = ANY(ARRAY['"); + "WHERE x.indexrelid = ANY(ARRAY["); ... - appendQualifiedRelation(&catalog_query, cell->val, conn, echo); + resetPQExpBuffer(&relbuf); + appendQualifiedRelation(&relbuf, cell->val, conn, echo); + appendStringLiteralConn(&catalog_query, relbuf.data, conn); -
vacuumdb --missing-stats-only(CVE-2026-116313, commite10d38d471): The catalog query runs with the session’s defaultsearch_pathand used an unqualified=. A database owner could create their own=operator and run code as the user runningvacuumdb. The fix rewrites the comparisons withOPERATOR(pg_catalog.=).
4. Logical replication from a malicious publisher (CVE-2026-120145)
A subscriber applies changes to whatever relation the publisher names. The subscriber only checked the relation kind and assumed the publisher would never offer a system catalog. A publisher that speaks the logical replication protocol does not have to be PostgreSQL, so a malicious one can name pg_catalog.pg_proc or pg_authid and have the apply worker write to it (commit cc2d1a4c98):
void
CheckSubscriptionTargetRel(Oid relid, const char *nspname,
const char *relname)
{
CheckSubscriptionRelkind(get_rel_relkind(relid), nspname, relname);
if (IsCatalogRelationOid(relid))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot use relation \"%s.%s\" as logical replication target",
nspname, relname),
errdetail("This operation is not supported for system tables.")));
if (get_rel_persistence(relid) == RELPERSISTENCE_TEMP)
ereport(ERROR, /* ... temporary tables ... */);
}
The commit notes the danger is limited to subscriptions owned by a superuser, because changes are applied as the table owner (or the subscription owner with run_as_owner) and a non-superuser never has write access to system catalogs. Superuser-owned subscriptions are common, though, especially in migration tooling. CVE-2026-120149 is a related “the remote side lies” bug: postgres_fdw and other foreign data wrappers could report inserting more rows than they were sent, and the executor then read past the end of its slot array.
5. PL/Perl and PL/Tcl re-entrancy
PL/Perl is a trusted language, so any role that can create functions in a database with plperl installed can reach its bugs. Five CVEs in this release are PL/Perl use-after-free, type confusion or stale-sigjmp_buf issues (CVE-2026-116307, 116309, 116319, 116320 and 116326), and CVE-2026-116323 is the PL/Tcl equivalent. The pattern is the same in each: a nested call frees memory that an outer call is still using. The spi_freeplan() fix (commit 250405afb7) adds a use count and refuses to free a running plan:
/*
* Refuse to free a query that is currently being executed. Its plan, and
* the query descriptor's plan_cxt (which holds the argument input
* functions and whatever they cache in their fn_mcxt), are still in use
* by the spi_exec_prepared() or spi_query_prepared() call running it.
*/
if (qdesc->use_count > 0)
croak("spi_freeplan: query is currently being executed");
The regression test added with the fix shows the trigger: a prepared query whose execution calls a function that runs spi_freeplan() on that same query.
Who found these bugs
The reporter credits stand out. In the fix commits on REL_18_STABLE, “Anthropic OSS program” appears as a reporter on 16 commits (among them the pg_dump, reindexdb, tsvector, PL/Perl and PL/Tcl fixes). Other commits credit “Claude and Ada Logics”, “OpenAI Codex Security” and the “OpenAI Security Research Team”, next to long-standing community contributors and independent researchers. AI-assisted review accounts for a visible share of a release that is already unusually large for a minor update.
Exposure indicators
There are no network IOCs for this release. These are the conditions that make an instance more exposed:
- Roles you do not fully trust can run
CREATEin any database (multi-tenant clusters, hosted notebooks, SQL playgrounds, BI tools with write access). plperl,pltclordblinkis installed in a database where untrusted roles can create functions.- You restore dumps or archives produced by another party, or dump databases whose table owners you do not control.
- You run superuser-owned subscriptions to publishers outside your control.
- Extensions are packaged separately from the server and were not rebuilt against the November minors.
Detection and remediation
# 1. Server and client versions (run on hosts, CI images and backup containers)
psql -XAtc 'SELECT version();'
pg_dump --version; pg_restore --version; reindexdb --version; vacuumdb --version
# 2. Container images and compose files pinned to vulnerable minors
rg -n 'postgres(ql)?:(14\.(1?[0-9]|2[0-4])|15\.(1?[0-9])|16\.(1[0-5]|[0-9])|17\.(1[01]|[0-9])|18\.[0-6])\b' \
Dockerfile* docker-compose*.yml .github/workflows/ k8s/ helm/ 2>/dev/null
# 3. Trusted procedural languages and dblink that widen the attack surface
psql -XAtc "SELECT extname, extversion FROM pg_extension WHERE extname IN ('plperl','pltcl','dblink','postgres_fdw','dict_int');"
# 4. Subscriptions owned by superusers (CVE-2026-120145)
psql -XAtc "SELECT s.subname, r.rolname, r.rolsuper, s.subconninfo FROM pg_subscription s JOIN pg_roles r ON r.oid = s.subowner;"
# 5. RPM-based hosts: confirm the server and every extension moved together
rpm -qa | rg -i 'postgresql(1[4-9])' | sort
- Upgrade the server to 18.7, 17.12, 16.16 or 15.20. A dump and reload is not required. Plan a move off 14: 14.25 is its final release.
- Upgrade extensions in the same change. On PGDG RPM repositories, update the server and all
*_NNextension packages in one transaction. For self-built extensions (TimescaleDB, PostGIS, pgvector, Citus and so on), rebuild against the new headers or use the vendor’s November builds. An old extension will load but will not carry theCVE-2026-6460checks. - Upgrade client binaries. Replace
pg_dump,pg_restore,reindexdbandvacuumdbeverywhere they run, not only on database hosts. Until then, do not restore archives or tar-formatrestore.sqlfiles from untrusted sources, and treat dumps of tenant-owned schemas as untrusted input. - Follow the post-upgrade steps in the release notes. The 18.7 notes list several index-corruption bugs: review the entries that mention
REINDEXand rebuild affected indexes. The notes also say existing incremental backup files should not be trusted, so take a new full backup after updating and base future incrementals on it. - Reduce who can create objects. Revoke
CREATEon schemas from roles that do not need it, dropplperl/pltclwhere unused, and avoid superuser-owned subscriptions to publishers you do not run.
Timeline
- 2025: PostgreSQL adds
\restrict/\unrestrictto dumps to fixCVE-2025-8714; the tar-formatrestore.sqlpath ships with a constant(null)key. - 2026-06-18 to 2026-11-09: Fix commits for the 52 CVEs land on all supported branches;
245eb832f(attribute versioning) is committed to master on 9 November. - 2026-11-09: 18.7 is stamped (
f5c41f6681). - 2026-11-12: The PostgreSQL Global Development Group publishes 18.7, 17.12, 16.16, 15.20, 14.25 and 19 RC 2. NVD publishes the CVE records at 15:17 UTC, the announcement is forwarded to
oss-security, and PGDG packagers ship rebuilt extensions alongside the new minors. PostgreSQL 14 reaches end-of-life.
From research to remediation
Check whether this pattern exists in your codebase
Turn this research into a remediation workflow. Use AI-native static analysis to find similar application vulnerabilities and generate review-ready fixes.
References
- PostgreSQL Global Development Group (12 November 2026): PostgreSQL 18.7, 17.12, 16.16, 15.20, 14.25, and 19 RC 2 Released
- PostgreSQL 18.7 release notes (reindex and incremental-backup guidance)
- PostgreSQL commit 245eb832f (master): Introduce attribute versioning to prevent unsafe deforming (CVE-2026-6460)
- PostgreSQL commit 9e4cde7927 (REL_18_STABLE): pg_dump: Quote not-null constraint names (CVE-2026-116318)
- PostgreSQL commit cc2d1a4c98 (REL_18_STABLE): Reject system catalogs as logical replication targets (CVE-2026-120145)
- oss-security (12 November 2026): PostgreSQL 18.7, 17.12, 16.16, 15.20, 14.25, and 19 RC 2 Released, fixes 52 security vulnerabilities
- Phoronix (12 November 2026): PostgreSQL Sees New Releases For 52 Security Vulnerabilities, 140+ Bug Fixes
- Planet PostgreSQL (12 November 2026): PGDG RPM packagers on updating the server and all extensions in the same transaction