From 7174222c87a07fbc0423f2b9bde3cea2cada311c Mon Sep 17 00:00:00 2001 From: Petr Khartskhaev Date: Thu, 4 Jun 2026 16:01:38 +0200 Subject: [PATCH] fix CVE-2026-6477 --- doc/src/sgml/libpq.sgml | 11 ++++++++--- src/interfaces/libpq/fe-exec.c | 18 ++++++++++++++++-- src/interfaces/libpq/fe-lobj.c | 12 ++++++------ src/interfaces/libpq/fe-protocol2.c | 16 +++++++++++++++- src/interfaces/libpq/fe-protocol3.c | 14 +++++++++++++- src/interfaces/libpq/libpq-int.h | 9 +++++++-- 6 files changed, 65 insertions(+), 15 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index cda4e8d..3e87183 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -5272,15 +5272,20 @@ int PQrequestCancel(PGconn *conn); to send simple function calls to the server. - + - This interface is somewhat obsolete, as one can achieve similar + This interface is unsafe and should not be used. When + result_is_int is set to 0, + PQfn may write data beyond the end of + result_buf, regardless of whether the buffer has + enough space for the requested number of bytes. Furthermore, it is + obsolete, as one can achieve similar performance and greater functionality by setting up a prepared statement to define the function call. Then, executing the statement with binary transmission of parameters and results substitutes for a fast-path function call. - + The function PQfnPQfn diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index ff101c4..5dea7e7 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -2661,6 +2661,20 @@ PQfn(PGconn *conn, int result_is_int, const PQArgBlock *args, int nargs) +{ + return PQnfn(conn, fnid, result_buf, -1, result_len, + result_is_int, args, nargs); +} + +/* + * PQnfn + * Private version of PQfn() with verification that returned data fits in + * result_buf when result_is_int == 0. Setting buf_size to -1 disables + * this verification. + */ +PGresult * +PQnfn(PGconn *conn, int fnid, int *result_buf, int buf_size, int *result_len, + int result_is_int, const PQArgBlock *args, int nargs) { *result_len = 0; @@ -2680,12 +2694,12 @@ PQfn(PGconn *conn, if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) return pqFunctionCall3(conn, fnid, - result_buf, result_len, + result_buf, buf_size, result_len, result_is_int, args, nargs); else return pqFunctionCall2(conn, fnid, - result_buf, result_len, + result_buf, buf_size, result_len, result_is_int, args, nargs); } diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c index 6866d64..b019b9e 100644 --- a/src/interfaces/libpq/fe-lobj.c +++ b/src/interfaces/libpq/fe-lobj.c @@ -288,8 +288,8 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len) argv[1].len = 4; argv[1].u.integer = (int) len; - res = PQfn(conn, conn->lobjfuncs->fn_lo_read, - (void *) buf, &result_len, 0, argv, 2); + res = PQnfn(conn, conn->lobjfuncs->fn_lo_read, + (void *) buf, len, &result_len, 0, argv, 2); if (PQresultStatus(res) == PGRES_COMMAND_OK) { PQclear(res); @@ -439,8 +439,8 @@ lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence) argv[2].len = 4; argv[2].u.integer = whence; - res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek64, - (void *) &retval, &result_len, 0, argv, 3); + res = PQnfn(conn, conn->lobjfuncs->fn_lo_lseek64, + (void *) &retval, sizeof(retval), &result_len, 0, argv, 3); if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8) { PQclear(res); @@ -605,8 +605,8 @@ lo_tell64(PGconn *conn, int fd) argv[0].len = 4; argv[0].u.integer = fd; - res = PQfn(conn, conn->lobjfuncs->fn_lo_tell64, - (void *) &retval, &result_len, 0, argv, 1); + res = PQnfn(conn, conn->lobjfuncs->fn_lo_tell64, + (void *) &retval, sizeof(retval), &result_len, 0, argv, 1); if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8) { PQclear(res); diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c index 0e36974..b4f0c10 100644 --- a/src/interfaces/libpq/fe-protocol2.c +++ b/src/interfaces/libpq/fe-protocol2.c @@ -1434,7 +1434,7 @@ pqEndcopy2(PGconn *conn) */ PGresult * pqFunctionCall2(PGconn *conn, Oid fnid, - int *result_buf, int *actual_result_len, + int *result_buf, int buf_size, int *actual_result_len, int result_is_int, const PQArgBlock *args, int nargs) { @@ -1516,6 +1516,20 @@ pqFunctionCall2(PGconn *conn, Oid fnid, } else { + /* + * If the server returned too much data for the + * buffer, something fishy is going on. Abandon ship. + */ + if (buf_size != -1 && *actual_result_len > buf_size) + { + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("server returned too much data\n")); + conn->asyncStatus = PGASYNC_READY; + pqDropConnection(conn, true); + conn->status = CONNECTION_BAD; + return pqPrepareAsyncResult(conn); + } + if (pqGetnchar((char *) result_buf, *actual_result_len, conn)) diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index c9f88ba..2931453 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -1893,7 +1893,7 @@ pqEndcopy3(PGconn *conn) */ PGresult * pqFunctionCall3(PGconn *conn, Oid fnid, - int *result_buf, int *actual_result_len, + int *result_buf, int buf_size, int *actual_result_len, int result_is_int, const PQArgBlock *args, int nargs) { @@ -2024,6 +2024,18 @@ pqFunctionCall3(PGconn *conn, Oid fnid, } else { + /* + * If the server returned too much data for the + * buffer, something fishy is going on. Abandon ship. + */ + 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); + } + if (pqGetnchar((char *) result_buf, *actual_result_len, conn)) diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 7ecbd55..1a073ff 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -606,6 +606,9 @@ extern void pqSaveMessageField(PGresult *res, char code, extern void pqSaveParameterStatus(PGconn *conn, const char *name, const char *value); extern int pqRowProcessor(PGconn *conn, const char **errmsgp); +extern PGresult *PQnfn(PGconn *conn, int fnid, int *result_buf, int buf_size, + int *result_len, int result_is_int, + const PQArgBlock *args, int nargs); /* === in fe-protocol2.c === */ @@ -619,7 +622,8 @@ extern int pqGetline2(PGconn *conn, char *s, int maxlen); extern int pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize); extern int pqEndcopy2(PGconn *conn); extern PGresult *pqFunctionCall2(PGconn *conn, Oid fnid, - int *result_buf, int *actual_result_len, + int *result_buf, int buf_size, + int *actual_result_len, int result_is_int, const PQArgBlock *args, int nargs); @@ -636,7 +640,8 @@ extern int pqGetline3(PGconn *conn, char *s, int maxlen); extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize); extern int pqEndcopy3(PGconn *conn); extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid, - int *result_buf, int *actual_result_len, + int *result_buf, int buf_size, + int *actual_result_len, int result_is_int, const PQArgBlock *args, int nargs); -- 2.54.0