Dev & EngARTICLE

Postgres 18.7 breaks extension ABI while fixing critical CVE

Postgres 18.7 breaks extension ABI while fixing critical CVE
Image: Roberto Diniz
Article · Dev & Eng
Reading
4 min
In short

The patch that fixes CVE-2026-6460 changes how Postgres internally interprets composite types, and this forces C extensions to be recompiled, under risk of them simply failing to load.

The release of Postgres 18.7, on November 12, 2026, brought something the community avoids at all costs: a binary compatibility (ABI) break within a minor version. The case was documented by DBA Jeremy Schneider, of the consultancy Ardent Performance Computing, who noticed the problem on the very day of the release when he saw extension build pipelines break in production.

The cause is CVE-2026-6460, one of several security fixes packaged in this minor release. It is not a footnote detail: it is the first item in the official release notes for version 18.7, which already indicates the weight the core team gave to the issue.

What the fix changes under the hood

The flaw concerned composite datums (values of ROW types or user-defined composite types) that could be interpreted with the wrong tuple descriptor. The core introduced versioning logic to detect this mismatch before it turns into silent data corruption or, worse, an exploitation vector.

Introduce versioning logic to permit detection of composite datums that don't match the tuple descriptor we wish to interpret them with. For full protection, this change requires rebuilding C-language extensions, and there are some extensions that will require source-code changes.

PostgreSQL 18.7 release notes

The problem is that this new check requires extensions written in C to see the updated internal structure of the tuple descriptor. Without recompilation, the extension keeps talking to the core using the old interface, and that's where the ABI breaks: a binary compiled against Postgres 18.6 won't necessarily run cleanly against 18.7.

For those who need an emergency bypass, the core added the composite_type_mismatch parameter, which disables the check. But the documentation is direct: doing so reintroduces the security risk that CVE-2026-6460 specifically closes. It's not a switch to flip in production without a headache afterward.

It's not the first time, and that matters

Schneider notes, in his own post, that this kind of break has happened before: version 17.1 had a similar episode, documented in an EnterpriseDB explainer about that minor's ABI breakage. Postgres API and ABI stability has official documentation dedicated to the topic, and a 2024 post by David Wheeler on the subject, cited by Schneider, already discussed the limits of that promise.

In short: Postgres tries to keep a stable core with a fast, innovative extension edge, and that's part of why the ecosystem is so strong. But when a serious CVE requires touching internal structures, that promise gives way, and this is the second documented time this has happened in a minor release in a short span.

The practical problem: broken lockstep

Theory is one thing; the field is another. Schneider reports that, in the very build pipelines he maintains for extensions running on CloudNativePG in Kubernetes, several broke on the same day as the release, because new extension packages arrived before the updated core.

Three examples cited directly by him:

  • AGE (a graph extension for Postgres) has already published a new version that doesn't work on version 18.6;
  • credcheck (credential validation) is in the same situation;
  • pg_cron (job scheduler) also released a build incompatible with 18.6.

The pattern identified by Schneider is consistent: updated extension versions use the new internal interface, and that's why they don't run on an old core. Older extensions, on the other hand, keep working on the new core. In other words, the direction of the upgrade matters, a lot.

Upgrade order is not optional

For those who manage environments with critical extensions, the operational lesson is clear and needs to become routine, not firefighting:

  1. Update the Postgres core to 18.7 first, in isolation, validating that the service comes up normally;
  2. Only afterward update the extension packages, confirming the version compatible with the new minor;
  3. Never let the package channel apply a new extension before the core, especially in automated CI/CD pipelines;
  4. Treat composite_type_mismatch as a documented emergency valve, not as a default setting.

Schneider mentions that Devrim Gunduz, maintainer of the Postgres RPM packages, published a post the same day (according to Schneider himself, more detailed than his own) explaining that RPM packaging now includes a dependency header that requires the specific core minor version. This reduces the risk of someone installing a new extension over an outdated core via yum/dnf, but Schneider did not confirm whether the Debian/APT ecosystem already has equivalent protection.

Where this hits hardest

The scenario described is more critical in environments with aggressive security patch automation: those who apply CVEs via pipeline without manual review run the real risk of updating the core and the extension out of order, or of getting stuck because the extension package arrived first in the repository. Teams running Postgres on Kubernetes with CloudNativePG, like Schneider himself, feel this more directly, because the container image build cycle tends to pull the latest available version of each component independently.

The common-sense recommendation, one that applies to any DBA who values consistency, is to treat a Postgres minor update like a schema change: planned, tested in staging with the environment's real extensions, and applied in lockstep, never component by component according to the pipeline's convenience. The security that CVE-2026-6460 delivers is only worth something if the extensions in production keep working the next day.

Translated from the Brazilian Portuguese original · Read the original