Lukas Fittl: Postgres 18.7 Security Release: The Impact of CVE-2026-6460 & other memory corruption issues

2026-11-12来源 Planet PostgreSQLEnglish

In today’s E123 of “5mins of Postgres” we’re talking about the Postgres 18.7, 17.12, 16.16, 15.20 and 14.25 minor releases that were released today, which fix 52 security vulnerabilities and other bugs in Postgres. In this episode we focus on the security impact of the release, but the release also contains other important fixes.

To help you assess when you should upgrade your system we group the CVEs by what an attacker needs to exploit them, take a closer look at CVE-2026-6460 and its unusually invasive fix, and discuss why this minor release can break extensions and needs extra care when you update your packages.

The quick TLDR: If you’re currently running Postgres and you allow arbitrary SQL to be executed in some way (or SQL injection is a potential concern), you should update to this release. There is a risk that any user who can execute SQL in your database could ultimately become an OS user or the Postgres superuser because of these vulnerabilities.



Share this episode: Click here to share this episode on LinkedIn. Feel free to sign up for our newsletter and subscribe to our YouTube channel.


Overview of the types of vulnerabilities

This is the grouping of the different CVEs by how they might be exploited, as also seen in the video episode. Unless noted otherwise, an issue affects all supported major versions (14 through 18).

This table was put together with the help of Claude Fable 5.2, Opus 5.5 and human review of the issues as well as the source code.

Pre-auth denial of service

CVEIssueWho can trigger it
CVE-2026-116322A cancel request sent before login, timed so that authentication_timeout fires during cancel handling, can leak a spinlock, which restarts the whole server. 18-only.Anyone who can reach the port, no login needed.

Code execution through memory corruption

These are heap overflows, use-after-free bugs and type confusion bugs. These may let the attacker execute arbitrary code as the OS user running PostgreSQL.

Any database user

CVEIssue
CVE-2026-116315Heap buffer overflow in ts_delete() from a crafted tsvector
CVE-2026-116317Heap buffer overflow in ts_rank_cd() from a crafted tsvector (“may” execute code)
CVE-2026-120141Type confusion in set-returning functions that return different record types per row, e.g. json_populate_recordset()
CVE-2026-116304Use-after-free with anonymous RECORD types in parallel query
CVE-2026-120139Integer wraparound in in-memory tuple size calculations, via oversized tuple data
CVE-2026-116328Integer wraparound in generation memory contexts, after freeing an allocation larger than 1 GB. 16 and later only.

Users who can create objects

Note this includes temporary objects created in pg_temp, which by default is permitted for any user.

CVEIssue
CVE-2026-6460Changing a row type (e.g. ALTER TYPE ... ALTER ATTRIBUTE ... TYPE, ALTER TABLE ... SET DATA TYPE, or ADD COLUMN plus rollback) after a query has captured values of it makes PostgreSQL misread those values (type confusion). Works within a single session, e.g. with a prepared statement, with no race needed. Proof of concept available.
CVE-2026-116331, CVE-2026-116335, CVE-2026-18447Type confusion through custom aggregates (SORTOP), commutator/negator operator links, and SQL function output parameters of type any
CVE-2026-116306, CVE-2026-120151, CVE-2026-120144, CVE-2026-120142, CVE-2026-120148Type confusion from row types changing mid-operation (field assignment, PL/pgSQL record fields, the type cache), from dropped columns, and from extended statistics on anonymous record types. 120144 (type cache) is 18-only; 120142 (dropped columns) affects 17 and 18 only.
CVE-2026-116332, CVE-2026-120143, CVE-2026-120150, CVE-2026-116336Use of freed or dropped objects: closing a portal while it’s being set up, cursors on trigger transition tables, expanded-record domain checks, and DROP INDEX from an index expression during REINDEX CONCURRENTLY
CVE-2026-116329Type confusion in statistics import. Needs table maintainer rights (MAINTAIN). 18-only.
PL/Perl: CVE-2026-116307, CVE-2026-116319, CVE-2026-116320, CVE-2026-116309, CVE-2026-116326. PL/Tcl: CVE-2026-116323Use-after-free, type confusion and error-handling bugs, if plperl/pltcl is installed (trusted languages are open to PUBLIC by default). For 116309 (type confusion) the advisory says “may”. For 116326 (an uncaught Perl exception that leads to use of a stale sigjmp_buf) code execution is “not ruled out”.
CVE-2026-120147Use-after-free on re-entrant dblink disconnect, if dblink is installed

Privilege escalation without memory corruption

These let an attacker get their own SQL run with someone else’s privileges, often a superuser’s.

CVEIssueWho can trigger it
CVE-2026-6467CREATE EXTENSION or ALTER EXTENSION for a trusted extension can be tricked into running attacker SQL as the bootstrap superuser, by renaming its schema or targeting "$user"Users with CREATE privilege on the database
CVE-2026-120153Same outcome through SQL injection in extension script substitutions, using schema or owner names with newlines and similarUsers with CREATE privilege on the database; only for non-bundled extensions with a vulnerable script pattern
CVE-2026-116316Security-restricted code (e.g. index functions) could create types in pg_temp. Later, the victim’s own session can resolve an unqualified type name to them and run attacker SQL. Autovacuum and other common maintenance paths don’t do this, so they’re unaffected.Object creators, against a session that later runs queries with unqualified type names

Attacks from a compromised remote server

CVEIssueWho can trigger it
CVE-2026-120145Logical replication can write to system catalogs on the subscriber, leading to code executionA compromised publisher; only superuser-owned subscriptions are affected
CVE-2026-120149Heap buffer overflow in foreign batch inserts when the remote server reports more inserted rows than it was sent. With postgres_fdw this can also happen legitimately when a rule on the remote table expands an INSERT.A malicious or misconfigured foreign server

Data exposure, privilege check bypasses and denial of service

  • CVE-2026-16237: INSERT ... ON CONFLICT DO UPDATE on an auto-updatable view lets a user with INSERT and UPDATE on the view read and modify every row of the base table, ignoring the view’s WHERE clause (even for security_barrier views).
  • CVE-2026-120154: INSERT ... ON CONFLICT DO UPDATE lets a user with INSERT, UPDATE and SELECT on a table read every row despite row-level security policies.
  • CVE-2026-116311: DROP OWNED lets a role remove itself from restrictive row-level security policies on tables it doesn’t own, and then read or modify rows the policy should deny.
  • CVE-2026-116327: values hidden by a security_barrier view can be inferred by applying a leaky function to indirect expressions.
  • CVE-2026-120146: REASSIGN OWNED lets a role with INHERIT but not SET transfer objects to the granted role, and reassign database ownership without CREATEDB.
  • CVE-2026-14661: the sslkeylogfile connection option in dblink and subscriptions lets a user append to a file of their choosing. The data can’t be arbitrary, but it can corrupt files and cause persistent denial of service. Also affects pre-18 servers that use libpq 18.
  • CVE-2026-116312: a MERGE on a partitioned table that inserts before it updates can make an AFTER trigger process the wrong new row. 15 and later only.
  • CVE-2026-116303: cast functions run inside JSON conversion and foreign key checks skipped the EXECUTE privilege check. The advisory notes there are few or no practical examples where this matters.
  • Server memory disclosure:
    • CVE-2026-116333: an object creator can read attacker-chosen memory addresses through parallel btree scans with array keys. 17 and 18 only.
    • Any database user can read portions of server memory through:

Client tool vulnerabilities

Mitigating these CVEs requires updating the client tools that run against a Postgres server.

  • CVE-2026-116314: a crafted non-plain-format archive can make pg_restore execute arbitrary code on the machine running it. Restoring an untrusted archive was always expected to run its SQL at the destination, but not to run code in the pg_restore process.
  • CVE-2026-116330: tar-format pg_dump output from a compromised server can run OS commands through psql when its restore.sql is used. This is a gap in the earlier fix for CVE-2025-8714.
  • CVE-2026-116313: vacuumdb --missing-stats-only lets a database owner run SQL as the user running vacuumdb. 18-only. The pg_upgrade documentation recommends an affected invocation, so in our assessment it’s likely to run with superuser rights after upgrades.
  • CVE-2026-116318: an object creator can inject SQL into pg_dump output through a NOT NULL constraint name. It runs as the user restoring the dump. 18-only.
  • CVE-2026-116325: an object creator can inject SQL through a crafted index name. It runs as the user running reindexdb, and only with --jobs together with --index. 17 and 18 only.
  • CVE-2026-16240: pg_basebackup can use backup manifest content as a format string. Only an already-compromised server can exploit it, and only to expose pg_basebackup’s own memory, which isn’t sent back to the attacker’s server. Low impact.

Transcript

Welcome back to 5mins of Postgres! Today we’re going to talk about the Postgres minor releases that were released today, that fix important security vulnerabilities and other bugs in Postgres.

If you look at the public announcement, one thing that stands out is that there are a lot of CVEs in this announcement. So many that it’s really hard to get a sense for what’s going on. We all know that AI tools make it easier to find issues, and Postgres is pretty good software, but it’s complex. And so the last releases have been pretty busy, and I think this one is one of the busiest this year.

Making sense of 52 CVEs

Now, I personally find the announcement really hard to read, so what I end up doing is first looking at the release notes. The release notes are oftentimes a little bit better, because they’re a little bit closer to the explanation of what was going on, on the technical side.

But even this is pretty hard to parse. It’s just a lot of information, and it’s hard to really make sense of these problems. And so what I ended up doing is a combination of AI analysis and human analysis. Usually with security issues I wouldn’t necessarily want to publish that, because it also makes attackers’ lives easier.

But the truth is, everybody these days can just point their AI tools at the Postgres source and at these commits, and use that to figure out how these vulnerabilities might work, or in which cases there are problems. So I feel like at this point we need to be more transparent, and talk more about: Is this actually a problem? Should I be updating? And what exactly might happen if I don’t update?

The analysis I’m showing here (see the overview above) is a combination of Claude Fable, Claude Opus, and my own analysis and prompting based on the Postgres source, the release notes and the announcement, as well as some other blog posts that the folks who reported these vulnerabilities wrote.

Unauthenticated denial of service through cancel requests

First of all, if your Postgres is on a public port, there is one issue here that could cause a denial of service on that public Postgres instance. The way cancel requests are handled could make it so that even if somebody doesn’t authenticate to the database, they could still cause a denial of service.

So if you have a publicly hosted Postgres, you should probably take action on that one, just to prevent your Postgres from becoming unavailable. But that’s kind of a separate class.

Who can exploit the memory corruption issues?

The main class of issues are all related to either memory corruption, or in some cases, privilege escalations.

The first group are things where arbitrary code can be executed through memory corruption. There are no published exploits for most of these, so we don’t necessarily know: Is this actually a problem in practice? Could somebody really go and execute arbitrary code right now?

What we do know is that they would have to be a user on your database. So somebody has to either get SQL to run somehow, or maybe you’re limiting access to certain people and you have superuser restricted. A very common situation would be a managed service provider. If you’re Amazon RDS, for example, these things matter because you don’t actually give the customers full root access. And with these exploits, that would potentially be possible.

There is a group of issues here where certain functions have memory corruption issues. For example, ts_delete() has a heap buffer overflow. Any user can call ts_delete() on a tsvector input, and so that could potentially cause arbitrary code execution.

CVE-2026-6460: Type confusion with composite types

One of the most interesting issues here is CVE-2026-6460. This one has to do with composite types. It was reported by I think 20 different people, including both the OpenAI and Anthropic security programs. AI found it, humans found it too. I think it’s been known about for a couple of months, but the fix for it was pretty complicated.

If you want to know the details, Mehmet Ince, who has previously reported on some Postgres security issues, did a write-up earlier this week where he describes this type confusion issue, and how he turns it into RCE. He has some more specific details in his post, but it’s all relating to this particular CVE.

Now, there are a couple of other related issues, and it’s going to take too long to go through each of them. But they all have to do with being able to create objects, and then in some cases either a superuser reading that object, or just the same process reading the object, and through that causing memory corruption.

Getting a superuser to run the attacker’s SQL

Another class here is where memory corruption is not involved, but if the attacker can run arbitrary SQL, they can then, for example, get a superuser to do certain things.

You could probably mitigate some of these by doing some workarounds. But the problem with all these fixes is that there are just so many of them. Is it a good idea to apply 20 workarounds? Probably not. You should probably upgrade.

Compromised remote servers and client tools

There are a couple more things here worth pointing out. First of all, there are two CVEs that relate to a compromised remote server.

One of them has to do with logical replication. You have a server that listens to another server through logical replication, and for some reason the logical replication stream can’t be trusted. It’s kind of an edge case, but that can be a problem.

The other case is about foreign servers, like postgres_fdw foreign servers, behaving incorrectly, with the foreign server being malicious. Also somewhat unlikely, but if you have these kinds of setups, you should know about that.

The other thing worth pointing out, I would say, is the client tool vulnerabilities, because you might not think about those when you’re patching. You obviously run the Postgres server, or you have a managed service that offers the Postgres server, but you might also be running Postgres client tools.

For example, you might be running pg_restore. One of these CVEs specifically relates to you running pg_restore when the input to pg_restore, the archive that gets imported, can’t be trusted. In that situation you can have arbitrary code execution in the pg_restore binary locally, so that’s kind of a problem.

In summary, these are all pretty bad issues. You’re not going to have your server be compromised immediately, but you should fix these, because if somebody can do SQL injection, they might be able to compromise a lot more through these issues.

The invasive fix for CVE-2026-6460

Now, one of the things that you should know about is that the fix for CVE-2026-6460 was a pretty invasive fix. Usually, that is not something that would be done in a minor release.

You can get a sense for this if you look at the commit messages. Across the many commits that were done by the security team in Postgres, they’re introducing a whole separate layer to version how the internal Postgres tuples get stored. 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.

Now, with this new layer, Postgres will error out and tell you that the type definition changed and it can’t read the old version. You can see by the commit message how detailed this is. And this is a pretty invasive change.

Extensions may need source code changes

The problem with this change that you should be aware of is that, first of all, it can break extensions. Some extensions can’t just be rebuilt against this update. They actually have to be changed.

For example, pgrx just made a release that updates things to the new API that’s required. Some other extensions like PostGIS also need special handling, especially when it comes to pg_upgrade, because there are some edge cases now.

Restart the server when you update the packages

The other thing that’s worth pointing out is that when you’re upgrading your Postgres to this new minor release, you might actually run into a problem.

Imagine you’re upgrading your packages in your self-managed Postgres, and you update the extensions, but you don’t update the server itself yet. Or you might update the server binary, but you don’t actually restart the server.

What can happen if you’re unlucky is that, because this is an ABI break, a binary interface break, when Postgres tries to load a shared object, for example PL/pgSQL, you get an “undefined symbol” error:

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 

Basically, there’s a mismatch between the minor version that’s running in the background, and the minor version of those shared libraries.

What this means is: If you’re updating to this Postgres minor release, make sure that when you’re updating the extensions, you’re also, at that moment, updating and restarting your main server.

The PGDG Debian packages will actually behave correctly by default, because they stop the old server, then replace the files, and start it again. With the RPM packages, in our test at least, it looked like it’s possible that it would cause issues at least momentarily, because the binaries get updated, and only then the server gets restarted. So that might be a problem.

I would really recommend testing this in your own environment with your specific setup. For example, if you use something like Patroni, that’s going to work differently, or if you start your service manually. So just be careful, because it’s kind of uncommon for a minor release to cause this issue.

原文:https://www.a8q9.cn/en/article/lukas-fittl-postgres-18-7-security-release-the-impact-of-cve-2026-6460-other-mem-f16b82.html

相关文章