mirror of
https://github.com/postgres/postgres.git
synced 2026-05-21 01:37:50 -04:00
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
This commit is contained in:
parent
67dd6243dc
commit
be01364404
5 changed files with 46 additions and 12 deletions
|
|
@ -7016,15 +7016,20 @@ int PQrequestCancel(PGconn *conn);
|
|||
to send simple function calls to the server.
|
||||
</para>
|
||||
|
||||
<tip>
|
||||
<warning>
|
||||
<para>
|
||||
This interface is somewhat obsolete, as one can achieve similar
|
||||
This interface is unsafe and should not be used. When
|
||||
<parameter>result_is_int</parameter> is set to <literal>0</literal>,
|
||||
<function>PQfn</function> may write data beyond the end of
|
||||
<parameter>result_buf</parameter>, 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.
|
||||
</para>
|
||||
</tip>
|
||||
</warning>
|
||||
|
||||
<para>
|
||||
The function <function id="libpq-PQfn">PQfn</function><indexterm><primary>PQfn</primary></indexterm>
|
||||
|
|
|
|||
|
|
@ -3001,6 +3001,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;
|
||||
|
||||
|
|
@ -3029,7 +3043,7 @@ PQfn(PGconn *conn,
|
|||
}
|
||||
|
||||
return pqFunctionCall3(conn, fnid,
|
||||
result_buf, result_len,
|
||||
result_buf, buf_size, result_len,
|
||||
result_is_int,
|
||||
args, nargs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -271,8 +271,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);
|
||||
|
|
@ -412,8 +412,8 @@ lo_lseek64(PGconn *conn, int fd, int64_t 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);
|
||||
|
|
@ -566,8 +566,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);
|
||||
|
|
|
|||
|
|
@ -2155,7 +2155,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)
|
||||
{
|
||||
|
|
@ -2289,6 +2289,17 @@ 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)
|
||||
{
|
||||
libpq_append_conn_error(conn, "server returned too much data");
|
||||
handleFatalError(conn);
|
||||
return pqPrepareAsyncResult(conn);
|
||||
}
|
||||
|
||||
if (pqGetnchar(result_buf,
|
||||
*actual_result_len,
|
||||
conn))
|
||||
|
|
|
|||
|
|
@ -759,6 +759,9 @@ extern int pqRowProcessor(PGconn *conn, const char **errmsgp);
|
|||
extern void pqCommandQueueAdvance(PGconn *conn, bool isReadyForQuery,
|
||||
bool gotSync);
|
||||
extern int PQsendQueryContinue(PGconn *conn, const char *query);
|
||||
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-protocol3.c === */
|
||||
|
||||
|
|
@ -774,7 +777,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);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue