Dev & EngARTICLE

Postgres 18.7 fixes 52 CVEs, including severe type confusion flaw

Postgres 18.7 fixes 52 CVEs, including severe type confusion flaw
Image: Roberto Diniz
Article · Dev & Eng
Reading
6 min
Tables
1
Code
1
In short

Versions 18.7, 17.12, 16.16, 15.20, and 14.25, released today, close 52 vulnerabilities. The standout is CVE-2026-6460, which exploits column type changes to execute arbitrary code.

The PostgreSQL Global Development Group released versions 18.7, 17.12, 16.16, 15.20, and 14.25 today, a security release that closes 52 CVEs. The volume caught the attention of Lukas Fittl, creator of the podcast "5mins of Postgres," who dedicated episode 123 of the series to organizing the announcement by exploiter type instead of following the raw order of the official list.

In short: if the application allows any user to execute arbitrary SQL, or if there's a risk of SQL injection, the update is no longer optional. Fittl summarizes the practical risk: a regular user who can run SQL could, by chaining some of these flaws, escalate to operating system user or Postgres superuser.

Six risk classes, not 52 loose problems

Fittl, whose analysis combined Postgres commits, the official announcement text, the release notes, and assistance from AI models (Claude Fable and Opus, according to his own post), grouped the CVEs by what an attacker needs to have in hand to exploit them.

ClassWho needs to be involvedExample
Pre-authentication denial of serviceAnyone who can reach the port, without logging inCVE-2026-116322 (cancel request + authentication_timeout, version 18 only)
Code execution via memory corruptionAny database userCVE-2026-116315, ts_delete() with a malformed tsvector
Privilege escalation without memory corruptionUser with object-creation privilegeCVE-2026-6467, CREATE EXTENSION running SQL as bootstrap superuser
Attacks via compromised remote serverMalicious logical replication publisher or foreign serverCVE-2026-120145, CVE-2026-120149
Data exposure and check bypassUser with INSERT/UPDATE on a view or table with RLSCVE-2026-16237, CVE-2026-120154
Client toolsAnyone running pg_restore, pg_dump, vacuumdb, or reindexdbCVE-2026-116314, CVE-2026-116313

The row that worries Fittl most is the second one: any authenticated user, even without any privilege beyond connecting, can trigger functions like ts_delete() or ts_rank_cd() with a malformed tsvector and cause a heap buffer overflow. For managed providers like Amazon RDS, where the customer never has root access, this is exactly the kind of flaw that would normally be contained.

CVE-2026-6460: changing a column's type fools Postgres

The most discussed vulnerability in the batch is CVE-2026-6460, reported by about 20 people, including OpenAI's and Anthropic's security programs. The problem: operations like ALTER TYPE ... ALTER ATTRIBUTE ... TYPE, ALTER TABLE ... SET DATA TYPE, or an ADD COLUMN followed by a rollback change a type's definition after a query has already captured values of that type, for example in a prepared statement. Postgres doesn't notice the change and interprets the old bytes with the new definition, a classic type confusion.

Researcher Mehmet Ince published an analysis this week showing how to turn this type confusion into remote code execution, with a proof of concept available. The point Fittl highlights is that the attack doesn't depend on a race condition: it works within a single session, deterministically.

The problem was that you would write a tuple, a row version, with one definition of the type, then you would change the type definition, and Postgres wouldn't know that the type changed.

Lukas Fittl, creator of "5mins of Postgres"

An unusually invasive patch for a minor release

To close CVE-2026-6460, the Postgres security team introduced an entire versioning layer for internal tuple storage. Now, when Postgres detects that a type's definition has changed since the tuple was read, it aborts with an error instead of reinterpreting the old bytes with the new definition. This is an unusual behavior change for a minor release, which normally limits itself to targeted bugfixes without touching internal structure.

This kind of fix has a direct side effect on extensions compiled against Postgres's ABI. Two practical consequences, according to Fittl:

  • pgrx, the framework used to write extensions in Rust, has already released an updated version for the new API required by the change.
  • PostGIS also needs special handling, especially in pg_upgrade scenarios, where edge cases now require attention.

Anyone maintaining locally compiled extensions, rather than ones packaged by the distribution, should check whether the build is aligned with the new internal layout before restarting the server in production.

Restart the server, not just the packages

The ABI change creates a specific operational trap: if the server binary is updated but the running processes aren't restarted, or if the extensions are updated before the server binary, Postgres may fail to load a shared library, such as PL/pgSQL's, with an error like:

ERROR: could not load library "/usr/lib/postgresql/18/lib/plpgsql.so": /usr/lib/postgresql/18/lib/plpgsql.so: undefined symbol: heap_datum_update_type

The reason is simple: there's a mismatch between the minor version running in memory and the minor version of the shared libraries on disk. Anyone managing their own Postgres (outside a managed service) needs to plan the server restart as a mandatory part of the package update, not as an optional step.

Other fronts worth a targeted check

Beyond memory corruption, the batch covers points that are far from classic memory modeling but directly affect anyone using upsert patterns or RLS:

  • CVE-2026-16237 and CVE-2026-120154: an INSERT ... ON CONFLICT DO UPDATE on an auto-updatable view or on a table with row-level security can read and modify rows that the view's WHERE clause, or the RLS policy, should hide.
  • CVE-2026-116311: DROP OWNED allows a role to remove itself from restrictive RLS policies on tables it doesn't own.
  • CVE-2026-116314 and CVE-2026-116330: untrusted pg_restore files or pg_dump tar output can execute arbitrary code on the machine running the restore, going beyond what was already expected (running SQL on the target).
  • CVE-2026-116322: a cancel request sent before login, timed to coincide with the authentication_timeout trigger, brings down the entire server without requiring authentication. Affects only version 18.

Teams that use upsert as a write pattern, or that rely on RLS to isolate multi-tenant data, should review these two specific CVEs before assuming that the version update alone solves the underlying modeling problem: poorly designed RLS policies remain poorly designed policies after the patch.

What remains as an operational lesson

Fittl's episode reinforces a point that applies to any database operation: the risk surface of 52 CVEs isn't uniform, and treating all of them as "apply and pray" is worse than understanding which exploitation class applies to your environment. Anyone running third-party SQL, even filtered, is in the highest-exposure range. Anyone operating an isolated Postgres, without logical replication from unknown sources and without PL/Perl or PL/Tcl extensions open to the public, has a somewhat more comfortable window, but no reason to delay.

The practical recommendation drawn from the material: schedule a full server restart together with the package update, revalidate locally compiled extensions against the new ABI, and check whether any application routine depends on INSERT ... ON CONFLICT DO UPDATE on a view or table with RLS before considering the job done.

Translated from the Brazilian Portuguese original · Read the original