223 lines
7.9 KiB
Diff
223 lines
7.9 KiB
Diff
From 7174222c87a07fbc0423f2b9bde3cea2cada311c Mon Sep 17 00:00:00 2001
|
|
From: Petr Khartskhaev <pkhartsk@redhat.com>
|
|
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.
|
|
</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>PQfn</function><indexterm><primary>PQfn</primary></indexterm>
|
|
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
|
|
|