Automated Threat Modeling

PostgreSQL libpq CVE-2026-6477: Brief Summary of a Client Side Stack Buffer Overwrite via Large Object Functions

A short review of CVE-2026-6477, a high-severity stack buffer overwrite in PostgreSQL's libpq library that lets a malicious server superuser corrupt client memory in psql and pg_dump. Includes patch analysis and details on affected versions.

CVE Analysis

8 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-05-14

PostgreSQL libpq CVE-2026-6477: Brief Summary of a Client Side Stack Buffer Overwrite via Large Object Functions
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

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 client
  • lo_read(): Reads data from a large object descriptor into a caller-provided buffer
  • lo_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:

  1. A malicious actor controls a PostgreSQL server (or has superuser privileges on one).
  2. A victim connects to this server with an unpatched psql client and issues a \lo_export command, or an automated pipeline runs pg_dump against the server.
  3. Both \lo_export and pg_dump internally call lo_read(), which invokes PQfn() with result_is_int=0.
  4. The malicious server responds with a large object data payload that exceeds the size of the stack buffer the client allocated.
  5. PQfn() writes the entire server response into the buffer without any bounds checking, overwriting adjacent stack memory.
  6. 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 BranchVulnerable VersionsFixed Version
18Before 18.418.4
17Before 17.1017.10
16Before 16.1416.14
15Before 15.1815.18
14Before 14.2314.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

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization