PostgreSQL 18.7, 17.12, 16.16, 15.20, 14.25: vulnerabilities in one minor release
52 vulnerabilities fixed in a single release; 32 of them run code as the server's operating-system user from any logged-in role — so one SQL injection in a web application is enough to reach them.
Read from postgresql.org on 2026-11-13. This page reads one release. PostgreSQL is not yet a product this site checks version by version: there is no per-branch list, no version box, and rasoat host sees the distro's packages but not a container image.
What happened
52 vulnerabilities in one minor release — an unusually large batch for PostgreSQL, which usually fixes a handful at a time.
43 flaws are in the core, 6 in client tools (pg_dump, pg_restore, pg_basebackup, reindexdb, vacuumdb) and 3 in bundled extensions (plperl, pltcl, dblink, dict_int).
40 reach all five lines; the rest touch only the lines that carry the feature, so do not read 52 as the same number for every line.
32 carry CVSS 8.8: they need a logged-in role, but any role will do, and the result is code as the process's operating-system user.
Only 1 needs no login, and that one affects availability only.
PostgreSQL 14 gets its final release here and is end-of-life: no further security fix will come for that line.
The path in
- Any role, no superuser needed. A web application connects with its own role; one SQL injection in that application is enough to reach these flaws.
- A closed port does not remove the risk. The attacker goes through the application, not through port 5432.
- Client tools count too:
pg_restoreon an archive you did not produce,pg_dumpin tar format piped topsql,pg_basebackupreading a manifest — so cron backup jobs anddocker execare in scope. - Extensions: only where
plperl,pltcl,dblinkordict_intis installed. - PostgreSQL 14: end-of-life, so nothing after this release — for that line the answer is a major upgrade, not a minor one.
Which copy runs
- Docker and Compose: the image tag is the version.
postgres:16pulled on deploy day stays at that day's minor; a floating tag only helps afterdocker compose pullandup -d. - The distro's package:
postgresql-16from the repository; patch withapt, then restart the cluster. - Managed services (RDS, Cloud SQL, Supabase, Neon): the provider patches on its own schedule;
SELECT version()shows the build that actually runs. - Built from source or from your own image: nobody patches it for you.
Running is not installed
apt upgradereplaces files on disk; the postmaster keeps the old binary in memory until it restarts.SELECT version()answers "which build is running";dpkg -l postgresql-16does not.- With a container,
pullonly downloads the image;up -dcreates the new process.docker psstill shows the old image if you stopped at pull.
The fix for each line
| Line | Fixed in | Status |
|---|---|---|
| 18 | 18.7 | Supported |
| 17 | 17.12 | Supported |
| 16 | 16.16 | Supported |
| 15 | 15.20 | Supported |
| 14 | 14.25 | End of life 2026-11-12 |
After the upgrade, not only the upgrade
Replacing the binary is enough to fix the code, but this release also has data work. The announcement's Updating section, verbatim, so you can check against it:
All PostgreSQL update releases are cumulative. As with other minor releases, users are not required to dump and reload their database or use pg_upgrade in order to apply this update release; you may simply stop PostgreSQL and update its binaries.
If you suspect that a GIN index is affected by one of the corruption issues fixed in this release, you should reindex it, for example:
REINDEX INDEX your_index_name;If you use pg_trgm, you should reindex any GiST indexes that use the gist_trgm_ops operator class.
If you see missing chunk number errors when reading a table, you should reindex the table, which also reindexes its TOAST table, for example:
REINDEX TABLE your_table_name;If you use incremental backups, we recommend taking a new full backup after updating and using it as the basis for subsequent incremental backups.
If you use partitioned tables, the following query finds NOT NULL NO INHERIT constraints on partitioned tables:
SELECT conrelid::regclass, conname
FROM pg_constraint
WHERE contype = 'n' AND connoinherit
AND conrelid IN (SELECT oid FROM pg_class WHERE relkind = 'p');You can remove unwanted constraints with ALTER TABLE ... DROP CONSTRAINT .... Constraints you want to keep should be repaired with ALTER TABLE ... ALTER CONSTRAINT ... INHERIT, which will fail if any of the table's partitions contain NULL values in that column.
If you have B-tree indexes on bpchar columns (that is, columns declared as bpchar without a length), you should reindex them after updating. Columns declared as char(n) are not affected.
Users who have skipped one or more update releases may need to run additional post-update steps; please see the release notes from earlier versions for details.
For more details, please see the release notes .
If you use pg_trgm for search, the GiST REINDEX is the step most easily missed: search keeps working and quietly returns fewer rows.
The 52 vulnerabilities
Descriptions and CVSS scores are quoted in English from postgresql.org's CVE pages; each row links its page.
| CVSS | CVE | Summary | Component |
|---|---|---|---|
| 8.8 | CVE-2026-116304 | Parallel RECORD typmod use-after-free executes arbitrary code | core |
| 8.8 | CVE-2026-116306 | Composite-type change during field assignment executes arbitrary code | core |
| 8.8 | CVE-2026-116307 | plperl prepared-query use-after-free executes arbitrary code | core |
| 8.8 | CVE-2026-116309 | plperl blessed-reference type confusion may execute arbitrary code | core |
| 8.8 | CVE-2026-116314 | pg_restore executes arbitrary code due to missing non-plain archive validation | client |
| 8.8 | CVE-2026-116315 | tsvector_delete() heap buffer overflow executes arbitrary code | core |
| 8.8 | CVE-2026-116316 | Security-restricted operations can create types in pg_temp, which later queries of the session find | core |
| 8.8 | CVE-2026-116317 | ts_rank_cd() heap buffer overflow may execute arbitrary code | core |
| 8.8 | CVE-2026-116318 | pg_dump not-null constraint name allows SQL injection | client |
| 8.8 | CVE-2026-116319 | plperl reentrant return_next() use-after-free executes arbitrary code | core |
| 8.8 | CVE-2026-116320 | plperl array and hash conversion use-after-free executes arbitrary code | core |
| 8.8 | CVE-2026-116323 | pltcl reentrant result conversion use-after-free executes arbitrary code | core |
| 8.8 | CVE-2026-116326 | plperl tied-object exceptions follow stale sigjmp_buf | core |
| 8.8 | CVE-2026-116328 | Generation memory context integer wraparound executes arbitrary code | core |
| 8.8 | CVE-2026-116329 | Statistics import type confusion executes arbitrary code | core |
| 8.8 | CVE-2026-116330 | pg_dump tar restore.sql fails to block psql command injection | client |
| 8.8 | CVE-2026-116331 | Min/max aggregate type confusion executes arbitrary code | core |
| 8.8 | CVE-2026-116332 | Portal setup use-after-free executes arbitrary code | core |
| 8.8 | CVE-2026-116335 | Commutator or negator result-type confusion executes arbitrary code | core |
| 8.8 | CVE-2026-116336 | REINDEX CONCURRENTLY does not block DROP INDEX during index functions | core |
| 8.8 | CVE-2026-120139 | Tuple-size integer wraparound executes arbitrary code | core |
| 8.8 | CVE-2026-120141 | Materialized set-returning function type confusion executes arbitrary code | core |
| 8.8 | CVE-2026-120142 | Dropped-column type confusion executes arbitrary code | core |
| 8.8 | CVE-2026-120143 | Transition-table cursor use-after-free executes arbitrary code | core |
| 8.8 | CVE-2026-120144 | Type-cache invalidation gap executes arbitrary code | core |
| 8.8 | CVE-2026-120145 | Logical replication can write to subscriber system catalogs, executing arbitrary code | core |
| 8.8 | CVE-2026-120147 | dblink reentrant disconnect use-after-free executes arbitrary code | extension |
| 8.8 | CVE-2026-120149 | FDW batch-insert mismatch executes arbitrary code | core |
| 8.8 | CVE-2026-120150 | Expanded-record domain-check use-after-free executes arbitrary code | core |
| 8.8 | CVE-2026-120151 | plpgsql record-field type confusion executes arbitrary code | core |
| 8.8 | CVE-2026-18447 | Output parameter type confusion executes arbitrary code | core |
| 8.8 | CVE-2026-6460 | Type confusion after ALTER ... SET DATA TYPE executes arbitrary code | core |
| 8.1 | CVE-2026-120148 | Extended-statistics pseudo-type confusion executes arbitrary code | core |
| 8.1 | CVE-2026-14661 | dblink and subscriptions can append to arbitrary files | extension |
| 8 | CVE-2026-116325 | reindexdb index name quoting allows SQL injection | client |
| 7.2 | CVE-2026-116313 | vacuumdb --missing-stats-only lets a database owner execute code as the vacuumdb user | client |
| 7.2 | CVE-2026-120153 | Extension-script substitutions allow SQL injection through object names | core |
| 7.2 | CVE-2026-6467 | Extension script schema mishandling executes arbitrary SQL | core |
| 6.5 | CVE-2026-116333 | Parallel B-tree array scan reads beyond array bounds | core |
| 5.4 | CVE-2026-116311 | DROP OWNED can remove restrictive row security policies from non-owned tables | core |
| 5.4 | CVE-2026-116312 | MERGE can place the wrong row in a trigger transition table | core |
| 5.4 | CVE-2026-16237 | INSERT ... ON CONFLICT can read or modify rows despite auto-updatable view WHERE condition | core |
| 5.3 | CVE-2026-116322 | Pre-authentication cancel request can leak a spinlock | core |
| 4.3 | CVE-2026-116308 | dict_int reads beyond zero-length tokens | extension |
| 4.3 | CVE-2026-116310 | COPY error context uses freed field storage | core |
| 4.3 | CVE-2026-116321 | Unterminated name value discloses server memory | core |
| 4.3 | CVE-2026-116327 | security_barrier analysis can expose hidden values through indirect expressions | core |
| 4.3 | CVE-2026-120146 | REASSIGN OWNED bypasses role SET and CREATEDB checks | core |
| 4.3 | CVE-2026-120152 | pg_show_all_settings() use-after-free discloses memory | core |
| 4.3 | CVE-2026-120154 | INSERT ... ON CONFLICT can read rows despite row security policies | core |
| 3.7 | CVE-2026-16240 | pg_basebackup can use backup manifest content as a format string | client |
| 3.1 | CVE-2026-116303 | Foreign key and JSON conversions bypass EXECUTE privilege on cast functions | core |
What to do
- Move the line you run to its fixed release — the table above. A minor update needs no dump and reload.
- PostgreSQL 14: upgrade the major (
pg_upgrade, or dump and restore) to a supported line. The last release of 14 is not a fix. - Containers:
docker compose pullthenup -d, then checkSELECT version()inside the container. - Managed services: read the build in use; if the provider has not patched, there is no fix on your side, only mitigation in the application.
Check yourself
psql -c 'SELECT version();'Tells you: the build actually running, not the one installed
psql -c 'SHOW server_version;'Tells you: the version the server reports
psql -c 'SELECT pg_postmaster_start_time();'Tells you: when the postmaster started: before the package upgrade means the new binary is not loaded
docker exec <container> psql -U postgres -c 'SELECT version();'Tells you: the build inside the container, not on the host
apt list --installed 'postgresql-*'Tells you: the installed packages on Debian or Ubuntu — only half the answer
psql -c '\dx'Tells you: installed extensions: plperl, pltcl, dblink, dict_int are in this group
Sources
- https://www.postgresql.org/about/news/postgresql-187-1712-1616-1520-1425-and-19-rc-2-released-3442/
- https://www.postgresql.org/support/security/
- CVE-2026-116304
- CVE-2026-116306
- CVE-2026-116307
Written from the release announcement and postgresql.org's CVE pages; data read on 2026-11-13. Where they differ, postgresql.org wins.