From d0695c513d88a51e8fd71a2d59b05dfffc4c8590 Mon Sep 17 00:00:00 2001
From: John Naylor <john.naylor@postgresql.org>
Date: Wed, 21 Jan 2026 15:21:31 +0700
Subject: [PATCH v1 1/2] Silence clang ubsan warning in sort template

make_multirange() is sometimes called with NULL ranges and zero
count. This leads to a qsort with a NULL pointer for the input
array. The sort template short-cicuits during insertion sort with
zero count, so this doesn't cause a crash, but the calculation sets
off a warning in clang 21 sanitizers. We can silence the warning by
checking that there are at least two elements to sort, and only then
perform arithmetic on the input pointer. Since we now check the length
at the start of the function, also remove the analagous checks that
guarded against recursion/iteration.

Reviewed-by:
Reported-by: Alexander Lakhin <exclusion@gmail.com>
Tested-by:
Discussion: https://postgr.es/m/777bd201-6e3a-4da0-a922-4ea9de46a3ee@gmail.com
Backpatch-through:
---
 src/include/lib/sort_template.h | 40 +++++++++++++++++----------------
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/src/include/lib/sort_template.h b/src/include/lib/sort_template.h
index e02aa73cd4d..22b2092d03b 100644
--- a/src/include/lib/sort_template.h
+++ b/src/include/lib/sort_template.h
@@ -311,6 +311,14 @@ loop:
 	DO_CHECK_FOR_INTERRUPTS();
 	if (n < 7)
 	{
+		/*
+		 * Not strictly necessary, but a caller may pass a NULL pointer input
+		 * and zero length, and this silences warnings about applying offsets
+		 * to NULL pointers.
+		 */
+		if (n < 2)
+			return;
+
 		for (pm = a + ST_POINTER_STEP; pm < a + n * ST_POINTER_STEP;
 			 pm += ST_POINTER_STEP)
 			for (pl = pm; pl > a && DO_COMPARE(pl - ST_POINTER_STEP, pl) > 0;
@@ -387,29 +395,23 @@ loop:
 	if (d1 <= d2)
 	{
 		/* Recurse on left partition, then iterate on right partition */
-		if (d1 > ST_POINTER_STEP)
-			DO_SORT(a, d1 / ST_POINTER_STEP);
-		if (d2 > ST_POINTER_STEP)
-		{
-			/* Iterate rather than recurse to save stack space */
-			/* DO_SORT(pn - d2, d2 / ST_POINTER_STEP) */
-			a = pn - d2;
-			n = d2 / ST_POINTER_STEP;
-			goto loop;
-		}
+		DO_SORT(a, d1 / ST_POINTER_STEP);
+
+		/* Iterate rather than recurse to save stack space */
+		/* DO_SORT(pn - d2, d2 / ST_POINTER_STEP) */
+		a = pn - d2;
+		n = d2 / ST_POINTER_STEP;
+		goto loop;
 	}
 	else
 	{
 		/* Recurse on right partition, then iterate on left partition */
-		if (d2 > ST_POINTER_STEP)
-			DO_SORT(pn - d2, d2 / ST_POINTER_STEP);
-		if (d1 > ST_POINTER_STEP)
-		{
-			/* Iterate rather than recurse to save stack space */
-			/* DO_SORT(a, d1 / ST_POINTER_STEP) */
-			n = d1 / ST_POINTER_STEP;
-			goto loop;
-		}
+		DO_SORT(pn - d2, d2 / ST_POINTER_STEP);
+
+		/* Iterate rather than recurse to save stack space */
+		/* DO_SORT(a, d1 / ST_POINTER_STEP) */
+		n = d1 / ST_POINTER_STEP;
+		goto loop;
 	}
 }
 #endif
-- 
2.52.0

