high

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:

  1. 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 is CVE-2026-6460, a family of composite-type confusions reachable through ALTER ... SET DATA TYPE and similar DDL. Its fix adds a new “attribute versioning” safety layer and changes the extension ABI.
  2. Client tools and replication trusting their input. pg_dump emitted 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_restore trusted archive contents and could be driven into out-of-bounds writes and path traversal (CVE-2026-116314). The tar-format restore.sql always used (null) as its \restrict key, which undid the 2025 fix for CVE-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).
  3. Privilege and policy bypasses. Row security and view WHERE conditions could be sidestepped with INSERT ... ON CONFLICT (CVE-2026-16237, CVE-2026-120154), DROP OWNED could strip restrictive policies from tables the caller does not own (CVE-2026-116311), and dblink / subscription connection strings accepted sslkeylogfile from 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

ComponentCVEs (selection)AffectedFixed
PostgreSQL server 18.xAll 52 that apply to the branch< 18.718.7
PostgreSQL server 17.xMost, including CVE-2026-6460 and CVE-2026-116325< 17.1217.12
PostgreSQL server 16.x / 15.xMost, including CVE-2026-6460 and CVE-2026-116330< 16.16, < 15.2016.16, 15.20
PostgreSQL server 14.x (end-of-life)Those that reach back to 14< 14.2514.25 (final release)
PostgreSQL 19 pre-releaseAll< 19 RC 219 RC 2
pg_dumpCVE-2026-116318 (18 only), CVE-2026-116330 (all branches)Same as serverSame as server
pg_restoreCVE-2026-116314Same as serverSame as server
reindexdb / vacuumdbCVE-2026-116325 (17 and 18), CVE-2026-116313Same as serverSame as server
Extensions built from PostgreSQL headersCVE-2026-6460 (ABI change)Builds against pre-November minorsRebuilds 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 symbol error.

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, commit 673ef2341e): 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 running pg_restore.

  • Tar-format restore.sql (CVE-2026-116330, commit 17e57d06cd): The 2025 fix for CVE-2025-8714 added \restrict / \unrestrict so a malicious server could not smuggle psql meta-commands into a dump. In the tar format’s undocumented restore.sql member, 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=tar as it already did for plain format.

  • reindexdb --jobs --index (CVE-2026-116325, commit aac5e6f503): 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, commit e10d38d471): The catalog query runs with the session’s default search_path and used an unqualified =. A database owner could create their own = operator and run code as the user running vacuumdb. The fix rewrites the comparisons with OPERATOR(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 CREATE in any database (multi-tenant clusters, hosted notebooks, SQL playgrounds, BI tools with write access).
  • plperl, pltcl or dblink is 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
  1. 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.
  2. Upgrade extensions in the same change. On PGDG RPM repositories, update the server and all *_NN extension 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 the CVE-2026-6460 checks.
  3. Upgrade client binaries. Replace pg_dump, pg_restore, reindexdb and vacuumdb everywhere they run, not only on database hosts. Until then, do not restore archives or tar-format restore.sql files from untrusted sources, and treat dumps of tenant-owned schemas as untrusted input.
  4. Follow the post-upgrade steps in the release notes. The 18.7 notes list several index-corruption bugs: review the entries that mention REINDEX and 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.
  5. Reduce who can create objects. Revoke CREATE on schemas from roles that do not need it, drop plperl / pltcl where unused, and avoid superuser-owned subscriptions to publishers you do not run.

Timeline

  • 2025: PostgreSQL adds \restrict / \unrestrict to dumps to fix CVE-2025-8714; the tar-format restore.sql path 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