From 025d2832d7df80a6cdb3e87360dcf53ff64eb62e Mon Sep 17 00:00:00 2001
From: Ewan Young <kdbase.hack@gmail.com>
Date: Thu, 2 Jul 2026 17:48:15 +0800
Subject: [PATCH] Fix crash in satisfies_hash_partition() with NULL variadic
 array

satisfies_hash_partition() is not strict, so it can be reached with a
NULL variadic array argument, e.g.

    SELECT satisfies_hash_partition('hp'::regclass, 1, 0, VARIADIC NULL::int[]);

When the call is variadic, the function fetches the argument with
PG_GETARG_ARRAYTYPE_P(3) without first checking for NULL, which
dereferences a NULL pointer and crashes the server.

Commit f3b0897a1213 fixed a set of related NULL-handling problems in
this function but overlooked the NULL variadic array case.

Fix by returning false when the call is variadic and the array argument
is NULL, mirroring how NULL parent OID, modulus, and remainder are
handled.  The NULL check must be gated on the call being variadic: for a
non-variadic call, a NULL fourth argument is a legitimate NULL value for
the first partitioning column (this is how the hash-partition CHECK
constraint routes rows with NULL key columns), and must continue to be
accepted rather than turned into a false result.

Back-patch to all supported branches, as f3b0897a1213 was.
---
 src/backend/partitioning/partbounds.c   | 12 ++++++++++--
 src/test/regress/expected/hash_part.out |  8 ++++++++
 src/test/regress/sql/hash_part.sql      |  4 ++++
 3 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 6fb150a8763..754d3077624 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -4781,8 +4781,16 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 	ColumnsHashData *my_extra;
 	uint64		rowHash = 0;
 
-	/* Return false if the parent OID, modulus, or remainder is NULL. */
-	if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2))
+	/*
+	 * Return false if the parent OID, modulus, or remainder is NULL, or if the
+	 * call is variadic and the variadic array argument itself is NULL.  (In a
+	 * non-variadic call, an individual NULL key value is legal and handled
+	 * below; likewise NULL elements of a non-NULL variadic array.  But a NULL
+	 * variadic array has no elements to deconstruct, so treat it like the
+	 * other NULL fixed arguments.)
+	 */
+	if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) ||
+		(get_fn_expr_variadic(fcinfo->flinfo) && PG_ARGISNULL(3)))
 		PG_RETURN_BOOL(false);
 	parentId = PG_GETARG_OID(0);
 	modulus = PG_GETARG_INT32(1);
diff --git a/src/test/regress/expected/hash_part.out b/src/test/regress/expected/hash_part.out
index cb39161f867..ce58b20b382 100644
--- a/src/test/regress/expected/hash_part.out
+++ b/src/test/regress/expected/hash_part.out
@@ -95,6 +95,14 @@ ERROR:  number of partitioning columns (2) does not match number of partition ke
 SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
 								variadic array[now(), now()]);
 ERROR:  column 1 of the partition key has type "integer", but supplied value is of type "timestamp with time zone"
+-- NULL variadic array must not crash; should be false
+SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
+								variadic NULL::int[]);
+ satisfies_hash_partition 
+--------------------------
+ f
+(1 row)
+
 -- check satisfies_hash_partition passes correct collation
 create table text_hashp (a text) partition by hash (a);
 create table text_hashp0 partition of text_hashp for values with (modulus 2, remainder 0);
diff --git a/src/test/regress/sql/hash_part.sql b/src/test/regress/sql/hash_part.sql
index 6e2c1f21bfc..279b780cd48 100644
--- a/src/test/regress/sql/hash_part.sql
+++ b/src/test/regress/sql/hash_part.sql
@@ -75,6 +75,10 @@ SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
 SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
 								variadic array[now(), now()]);
 
+-- NULL variadic array must not crash; should be false
+SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
+								variadic NULL::int[]);
+
 -- check satisfies_hash_partition passes correct collation
 create table text_hashp (a text) partition by hash (a);
 create table text_hashp0 partition of text_hashp for values with (modulus 2, remainder 0);
-- 
2.47.3

