Introduction
PostgreSQL has finally fixed a long-standing unsafe API pattern in its libpq client library, after researchers demonstrated that a malicious server superuser could overwrite client stack memory during routine large object operations. For any organization running psql or pg_dump against databases it does not fully control, CVE-2026-6477 is a direct path from a compromised server to arbitrary code execution on the client machine.
The vulnerability, scored at CVSS 8.8, affects the PQfn() function that underpins large object handling in libpq. PostgreSQL is the most popular database among developers according to the 2025 Stack Overflow Developer Survey (47 percent want to use it, and 66 percent of current users want to continue). Vulnerable libpq binaries therefore sit across CI pipelines, backup infrastructure, and administrative workstations, and the blast radius is substantial.
Technical Information
The Root Cause: An Unfixable Public API
CVE-2026-6477 sits in the PQfn() function in libpq (src/interfaces/libpq/fe-exec.c). When PQfn() is called with result_is_int=0 (a non-integer result type), nothing tells it how large the output buffer is. Without that size, the function cannot check whether the data the server returns will fit into the allocated memory. It simply writes whatever the server sends into result_buf, with no bounds check.
This is conceptually identical to C's infamous gets() function, which was removed from the C11 standard precisely because it stores arbitrary-length input into a buffer of unspecified size. The PostgreSQL advisory draws the same parallel: "Like gets(), PQfn(..., result_is_int=0, ...) stores arbitrary-length, server-determined data into a buffer of unspecified size."
Vulnerable Large Object Functions
Several libpq large object functions expose the dangerous PQfn() code path:
lo_export(): Exports a large object to a file on the clientlo_read(): Reads data from a large object descriptor into a caller-provided bufferlo_lseek64(): Seeks within a large object (64-bit variant for objects exceeding 2GB)lo_tell64(): Returns the current position within a large object (64-bit variant)
Each of these functions calls PQfn() with result_is_int=0 and passes a stack-allocated buffer whose size never reaches the underlying protocol handler.
lo_read() rejects length values larger than INT_MAX at the application layer, but that check is irrelevant to the vulnerability. It limits how much data the client requests, and the server decides how much comes back. A malicious server can respond with an arbitrarily large payload regardless of the requested read size, and PQfn() will dutifully write all of it into the undersized buffer.
Attack Flow
Exploitation proceeds as follows:
- A malicious actor controls a PostgreSQL server (or has superuser privileges on one).
- A victim connects to this server with an unpatched
psqlclient and issues a\lo_exportcommand, or an automated pipeline runspg_dumpagainst the server. - Both
\lo_exportandpg_dumpinternally calllo_read(), which invokesPQfn()withresult_is_int=0. - The malicious server responds with a large object data payload that exceeds the size of the stack buffer the client allocated.
PQfn()writes the entire server response into the buffer without any bounds checking, overwriting adjacent stack memory.- The attacker corrupts stack memory on the client machine, which may lead to arbitrary code execution.
The CVSS vector AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H matches this scenario. The attack is network accessible and low in complexity, and the attacker needs no privileges because they control the server. It does require user interaction, since the victim must connect to the malicious server. Confidentiality, integrity, and availability impact are all rated high.
Why This Is a Client-Side Vulnerability
The overflow happens entirely inside the client process, not on the server. The server is the attacker and the client is the victim. That inverts the typical threat model for database vulnerabilities, where the server is usually the target. Organizations that connect administrative tools to third-party, cloud-hosted, or otherwise untrusted PostgreSQL instances are at particular risk.
Patch Information
The PostgreSQL Global Development Group fixed CVE-2026-6477 in a coordinated release on May 14, 2026, shipping fixes in PostgreSQL versions 18.4, 17.10, 16.14, 15.18, and 14.23 (backpatched through version 14). The fix landed in commit 8ac723b2b, authored by Nathan Bossart and committed by Noah Misch.
Changing the PQfn() function signature would break its public API contract, so the patch takes a pragmatic two-pronged approach: it deprecates the public function and introduces a safe internal replacement.
New Internal Function: PQnfn()
In src/interfaces/libpq/fe-exec.c, the patch introduces a private function PQnfn() that mirrors PQfn() but accepts an additional buf_size parameter:
PGresult * PQnfn(PGconn *conn, int fnid, int *result_buf, int buf_size, int *result_len, int result_is_int, const PQArgBlock *args, int nargs)
The original PQfn() stays for backward compatibility, but it now simply delegates to PQnfn() with buf_size set to -1, which means no size verification. Third-party callers keep the old (unsafe) behavior, while all internal code moves to the safe path.
Buffer Overflow Guard in the Protocol Layer
In src/interfaces/libpq/fe-protocol3.c, the pqFunctionCall3() function also gains the new buf_size parameter. The patch adds a new check right before data is copied into the result buffer:
if (buf_size != -1 && *actual_result_len > buf_size) { appendPQExpBufferStr(&conn->errorMessage, libpq_gettext("server returned too much data\n")); handleSyncLoss(conn, id, *actual_result_len); return pqPrepareAsyncResult(conn); }
This check is the core of the fix. If the server responds with more data than the buffer can hold, libpq declares the connection lost and the call aborts safely before the overrun can happen.
Migration of In-Tree Callers
The only in-tree callers of the old PQfn() were the libpq large object functions. The patch updates each of them in src/interfaces/libpq/fe-lobj.c to call PQnfn() instead, with the correct buffer size. For example, lo_read() passes the caller-supplied len, while lo_lseek64() and lo_tell64() pass sizeof(retval) for their 8-byte return values.
Documentation Update
In doc/src/sgml/libpq.sgml, the old tip calling PQfn() "somewhat obsolete" is gone. A warning block now states that the interface is unsafe and should not be used, and names the buffer overwrite risk when result_is_int is 0.
Affected Systems and Versions
The following PostgreSQL versions are affected:
| Major Branch | Vulnerable Versions | Fixed Version |
|---|---|---|
| 18 | Before 18.4 | 18.4 |
| 17 | Before 17.10 | 17.10 |
| 16 | Before 16.14 | 16.14 |
| 15 | Before 15.18 | 15.18 |
| 14 | Before 14.23 | 14.23 |
The vulnerability affects the client-side libpq library and any application or tool that links against it. The most prominent affected tools are psql (via the \lo_export meta-command) and pg_dump (via lo_read()). Any custom application that uses the libpq large object functions (lo_export(), lo_read(), lo_lseek64(), lo_tell64()) or calls PQfn() directly with result_is_int=0 is also vulnerable.
To fully remediate the risk, administrators must make sure every client binary links to the updated libpq library. Patching only the server is not enough, because the vulnerability is on the client side.
Vendor Security History
The PostgreSQL project has a mature, coordinated security response process. The May 2026 update cycle addressed 11 security vulnerabilities alongside more than 60 bug fixes, and the project consistently ships patches for all supported major versions at the same time. Security researchers Yu Kunpeng and Martin Heistermann discovered CVE-2026-6477 and reported it responsibly.
References
- PostgreSQL Security Advisory: CVE-2026-6477
- PostgreSQL 18.4, 17.10, 16.14, 15.18, and 14.23 Released!
- Documentation: 18: E.1. Release 18.4
- Documentation: 17: Release 17.10
- Patch Commit 8ac723b2b
- PostgreSQL Documentation: 18: 33.3. Client Interfaces (Large Objects)
- NVD Entry: CVE-2026-6477
- PostgreSQL on Wikipedia
- Stack Overflow 2025 Developer Survey



