Skip to content

Commit 4792641

Browse files
nathan-bossartanju15bharti
authored andcommitted
Mark PQfn() unsafe and fix overrun in frontend LO interface.
When result_is_int is set to 0, PQfn() cannot validate that the result fits in result_buf, so it will write data beyond the end of the buffer when the server returns more data than requested. Since this function is insecurable and obsolete, add a warning to the top of the pertinent documentation advising against its use. The only in-tree caller of PQfn() is the frontend large object interface. To fix that, add a buf_size parameter to pqFunctionCall3() that is used to protect against overruns, and use it in a private version of PQfn() that also accepts a buf_size parameter. Reported-by: Yu Kunpeng <yu443940816@live.com> Reported-by: Martin Heistermann <martin.heistermann@unibe.ch> Author: Nathan Bossart <nathandbossart@gmail.com> Reviewed-by: Noah Misch <noah@leadboat.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Etsuro Fujita <etsuro.fujita@gmail.com> Security: CVE-2026-6477 Backpatch-through: 14 (cherry picked from commit be013644043e5bae7260c09ab49cc6d64b7992be)
1 parent e947458 commit 4792641

5 files changed

Lines changed: 46 additions & 12 deletions

File tree

doc/src/sgml/libpq.sgml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7016,15 +7016,20 @@ int PQrequestCancel(PGconn *conn);
70167016
to send simple function calls to the server.
70177017
</para>
70187018

7019-
<tip>
7019+
<warning>
70207020
<para>
7021-
This interface is somewhat obsolete, as one can achieve similar
7021+
This interface is unsafe and should not be used. When
7022+
<parameter>result_is_int</parameter> is set to <literal>0</literal>,
7023+
<function>PQfn</function> may write data beyond the end of
7024+
<parameter>result_buf</parameter>, regardless of whether the buffer has
7025+
enough space for the requested number of bytes. Furthermore, it is
7026+
obsolete, as one can achieve similar
70227027
performance and greater functionality by setting up a prepared
70237028
statement to define the function call. Then, executing the statement
70247029
with binary transmission of parameters and results substitutes for a
70257030
fast-path function call.
70267031
</para>
7027-
</tip>
7032+
</warning>
70287033

70297034
<para>
70307035
The function <function id="libpq-PQfn">PQfn</function><indexterm><primary>PQfn</primary></indexterm>

src/interfaces/libpq/fe-exec.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3001,6 +3001,20 @@ PQfn(PGconn *conn,
30013001
int result_is_int,
30023002
const PQArgBlock *args,
30033003
int nargs)
3004+
{
3005+
return PQnfn(conn, fnid, result_buf, -1, result_len,
3006+
result_is_int, args, nargs);
3007+
}
3008+
3009+
/*
3010+
* PQnfn
3011+
* Private version of PQfn() with verification that returned data fits in
3012+
* result_buf when result_is_int == 0. Setting buf_size to -1 disables
3013+
* this verification.
3014+
*/
3015+
PGresult *
3016+
PQnfn(PGconn *conn, int fnid, int *result_buf, int buf_size, int *result_len,
3017+
int result_is_int, const PQArgBlock *args, int nargs)
30043018
{
30053019
*result_len = 0;
30063020

@@ -3029,7 +3043,7 @@ PQfn(PGconn *conn,
30293043
}
30303044

30313045
return pqFunctionCall3(conn, fnid,
3032-
result_buf, result_len,
3046+
result_buf, buf_size, result_len,
30333047
result_is_int,
30343048
args, nargs);
30353049
}

src/interfaces/libpq/fe-lobj.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,8 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len)
271271
argv[1].len = 4;
272272
argv[1].u.integer = (int) len;
273273

274-
res = PQfn(conn, conn->lobjfuncs->fn_lo_read,
275-
(void *) buf, &result_len, 0, argv, 2);
274+
res = PQnfn(conn, conn->lobjfuncs->fn_lo_read,
275+
(void *) buf, len, &result_len, 0, argv, 2);
276276
if (PQresultStatus(res) == PGRES_COMMAND_OK)
277277
{
278278
PQclear(res);
@@ -412,8 +412,8 @@ lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence)
412412
argv[2].len = 4;
413413
argv[2].u.integer = whence;
414414

415-
res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek64,
416-
(void *) &retval, &result_len, 0, argv, 3);
415+
res = PQnfn(conn, conn->lobjfuncs->fn_lo_lseek64,
416+
(void *) &retval, sizeof(retval), &result_len, 0, argv, 3);
417417
if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8)
418418
{
419419
PQclear(res);
@@ -566,8 +566,8 @@ lo_tell64(PGconn *conn, int fd)
566566
argv[0].len = 4;
567567
argv[0].u.integer = fd;
568568

569-
res = PQfn(conn, conn->lobjfuncs->fn_lo_tell64,
570-
(void *) &retval, &result_len, 0, argv, 1);
569+
res = PQnfn(conn, conn->lobjfuncs->fn_lo_tell64,
570+
(void *) &retval, sizeof(retval), &result_len, 0, argv, 1);
571571
if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8)
572572
{
573573
PQclear(res);

src/interfaces/libpq/fe-protocol3.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2155,7 +2155,7 @@ pqEndcopy3(PGconn *conn)
21552155
*/
21562156
PGresult *
21572157
pqFunctionCall3(PGconn *conn, Oid fnid,
2158-
int *result_buf, int *actual_result_len,
2158+
int *result_buf, int buf_size, int *actual_result_len,
21592159
int result_is_int,
21602160
const PQArgBlock *args, int nargs)
21612161
{
@@ -2289,6 +2289,17 @@ pqFunctionCall3(PGconn *conn, Oid fnid,
22892289
}
22902290
else
22912291
{
2292+
/*
2293+
* If the server returned too much data for the
2294+
* buffer, something fishy is going on. Abandon ship.
2295+
*/
2296+
if (buf_size != -1 && *actual_result_len > buf_size)
2297+
{
2298+
libpq_append_conn_error(conn, "server returned too much data");
2299+
handleFatalError(conn);
2300+
return pqPrepareAsyncResult(conn);
2301+
}
2302+
22922303
if (pqGetnchar(result_buf,
22932304
*actual_result_len,
22942305
conn))

src/interfaces/libpq/libpq-int.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,9 @@ extern int pqRowProcessor(PGconn *conn, const char **errmsgp);
759759
extern void pqCommandQueueAdvance(PGconn *conn, bool isReadyForQuery,
760760
bool gotSync);
761761
extern int PQsendQueryContinue(PGconn *conn, const char *query);
762+
extern PGresult *PQnfn(PGconn *conn, int fnid, int *result_buf, int buf_size,
763+
int *result_len, int result_is_int,
764+
const PQArgBlock *args, int nargs);
762765

763766
/* === in fe-protocol3.c === */
764767

@@ -774,7 +777,8 @@ extern int pqGetline3(PGconn *conn, char *s, int maxlen);
774777
extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize);
775778
extern int pqEndcopy3(PGconn *conn);
776779
extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid,
777-
int *result_buf, int *actual_result_len,
780+
int *result_buf, int buf_size,
781+
int *actual_result_len,
778782
int result_is_int,
779783
const PQArgBlock *args, int nargs);
780784

0 commit comments

Comments
 (0)