This is an automated email from the ASF dual-hosted git repository.

reshke pushed a commit to branch REL_2_STABLE
in repository https://gitbox.apache.org/repos/asf/cloudberry.git

commit cbfe1048ed625722eb5aa59239dbcf112a443d31
Author: Nathan Bossart <[email protected]>
AuthorDate: Mon May 11 05:13:51 2026 -0700

    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 <[email protected]>
    Reported-by: Martin Heistermann <[email protected]>
    Author: Nathan Bossart <[email protected]>
    Reviewed-by: Noah Misch <[email protected]>
    Reviewed-by: Tom Lane <[email protected]>
    Reviewed-by: Etsuro Fujita <[email protected]>
    Security: CVE-2026-6477
    Backpatch-through: 14
---
 doc/src/sgml/libpq.sgml             | 11 ++++++++---
 src/interfaces/libpq/fe-exec.c      | 16 +++++++++++++++-
 src/interfaces/libpq/fe-lobj.c      | 12 ++++++------
 src/interfaces/libpq/fe-protocol3.c | 14 +++++++++++++-
 src/interfaces/libpq/libpq-int.h    |  6 +++++-
 5 files changed, 47 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index efd039381d6..eb9a9b80a69 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -5721,15 +5721,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>
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 031c89336e7..c1fbaa9138f 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -2920,6 +2920,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;
 
@@ -2947,7 +2961,7 @@ PQfn(PGconn *conn,
        }
 
        return pqFunctionCall3(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 ffd9926dc4e..9c18b6b119d 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -275,8 +275,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);
@@ -418,8 +418,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);
@@ -574,8 +574,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-protocol3.c 
b/src/interfaces/libpq/fe-protocol3.c
index 5311a40a147..4dd146a70e0 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -2156,7 +2156,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)
 {
@@ -2290,6 +2290,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 0cbd611bd98..0a05756cb1e 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -734,6 +734,9 @@ extern PGresult *pqFunctionCall2(PGconn *conn, Oid fnid,
 
 extern void pqCommandQueueAdvance(PGconn *conn);
 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 === */
@@ -749,7 +752,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);
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to