Hi,

While testing the current master with SQLsmith, I found a query that crashes
the backend.  After running the regression tests, the issue can be
reproduced in the regression database with:

postgres=# \c regression
You are now connected to database "regression" as user "ubuntu".
regression=# SELECT expanded_record_info(2);
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.

(gdb) bt
#0  0x000062d8180991a3 in VARATT_IS_EXTERNAL (PTR=0x2) at
../../../../src/include/varatt.h:403
#1  0x000062d8180991c5 in VARATT_IS_EXTERNAL_EXPANDED_RW (PTR=0x2) at
../../../../src/include/varatt.h:436
#2  0x000062d81809a654 in DatumGetExpandedRecord (d=2) at expandedrecord.c:847
#3  0x000076332abce2b7 in expanded_record_info (fcinfo=0x62d845e6d230)
at regress.c:475
#4  0x000062d817cce8ba in ExecInterpExpr (state=0x62d845e6cca0,
econtext=0x62d845e6c480, isnull=0x7ffcbda7663f) at
execExprInterp.c:987
#5  0x000062d817cd1502 in ExecInterpExprStillValid
(state=0x62d845e6cca0, econtext=0x62d845e6c480, isNull=0x7ffcbda7663f)
at execExprInterp.c:2309
#6  0x000062d817d3bae2 in ExecEvalExprSwitchContext
(state=0x62d845e6cca0, econtext=0x62d845e6c480, isNull=0x7ffcbda7663f)
at ../../../src/include/executor/executor.h:452
#7  0x000062d817d3bc33 in ExecQual (state=0x62d845e6cca0,
econtext=0x62d845e6c480) at
../../../src/include/executor/executor.h:539
#8  0x000062d817d3c01b in ExecScanExtended (node=0x62d845e6c370,
accessMtd=0x62d817d3c08a <SeqNext>, recheckMtd=0x62d817d3c16f
<SeqRecheck>,

The function has a polymorphic argument, so a scalar value such as an
integer is accepted during function resolution.  However,
expanded_record_info() passes the argument directly to
DatumGetExpandedRecord() through PG_GETARG_EXPANDED_RECORD(), assuming
that it is a composite Datum.  The integer value 2 is consequently treated
as a pointer, causing an access at address 0x2 and crashing the backend.

The same issue exists in expanded_record_identity() and
expanded_record_field_text(), since they make the same assumption about
their polymorphic arguments.

The attached patch checks that the resolved argument type is a row
type before converting the Datum.

-- 
Thanks,
Tender Wang
From 9130f943eec34b8f2df2fd287a991120b277f0ec Mon Sep 17 00:00:00 2001
From: Tender Wang <[email protected]>
Date: Tue, 10 Nov 2026 09:59:40 +0800
Subject: [PATCH] Validate expanded record test function arguments

The expanded-record regression test functions accept polymorphic
arguments, allowing them to be called with scalar values.  However, their
C implementations passed such values directly to
DatumGetExpandedRecord(), which treats the Datum as a pointer.  For
example, passing the integer 2 caused the backend to dereference address
0x2 and crash.

Check that the resolved argument type is a row type before converting the
Datum.  Apply the check to expanded_record_identity(),
expanded_record_info(), and expanded_record_field_text(), all of which
have the same issue.
---
 src/test/regress/regress.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index 6e61c0d201b..9c65b723a8a 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -440,6 +440,17 @@ make_tuple_indirect(PG_FUNCTION_ARGS)
        PG_RETURN_POINTER(newtup->t_data);
 }
 
+static ExpandedRecordHeader *
+get_test_expanded_record(FunctionCallInfo fcinfo, int argno)
+{
+       Oid                     argtype = get_fn_expr_argtype(fcinfo->flinfo, 
argno);
+
+       if (!OidIsValid(argtype) || !type_is_rowtype(argtype))
+               elog(ERROR, "argument must be of a composite type");
+
+       return DatumGetExpandedRecord(fcinfo->args[argno].value);
+}
+
 /*
  * Functions for testing expanded records built from a flat composite datum.
  *
@@ -456,7 +467,7 @@ PG_FUNCTION_INFO_V1(expanded_record_identity);
 Datum
 expanded_record_identity(PG_FUNCTION_ARGS)
 {
-       ExpandedRecordHeader *erh = PG_GETARG_EXPANDED_RECORD(0);
+       ExpandedRecordHeader *erh = get_test_expanded_record(fcinfo, 0);
 
        PG_RETURN_EXPANDED_RECORD(erh);
 }
@@ -472,7 +483,7 @@ PG_FUNCTION_INFO_V1(expanded_record_info);
 Datum
 expanded_record_info(PG_FUNCTION_ARGS)
 {
-       ExpandedRecordHeader *erh = PG_GETARG_EXPANDED_RECORD(0);
+       ExpandedRecordHeader *erh = get_test_expanded_record(fcinfo, 0);
        TupleDesc       tupdesc = expanded_record_get_tupdesc(erh);
 
        PG_RETURN_TEXT_P(cstring_to_text(psprintf("type %s, typmod is default: 
%s, natts %d",
@@ -489,7 +500,7 @@ PG_FUNCTION_INFO_V1(expanded_record_field_text);
 Datum
 expanded_record_field_text(PG_FUNCTION_ARGS)
 {
-       ExpandedRecordHeader *erh = PG_GETARG_EXPANDED_RECORD(0);
+       ExpandedRecordHeader *erh = get_test_expanded_record(fcinfo, 0);
        int                     fnum = PG_GETARG_INT32(1);
        TupleDesc       tupdesc = expanded_record_get_tupdesc(erh);
        Datum           val;
-- 
2.43.0

Reply via email to