v1-0001-Fix-two-defects-with-extended-statistics-for-expr.patch

text/plain

Filename: v1-0001-Fix-two-defects-with-extended-statistics-for-expr.patch
Type: text/plain
Part: 0
Message: Defects with invalid stats data for expressions in extended stats

Patch

Format: format-patch
Series: patch v1-0001
Subject: Fix two defects with extended statistics for expressions
File+
src/backend/statistics/extended_stats.c 20 0
src/backend/utils/adt/selfuncs.c 5 1
From 69e219ef17c5100911851dcba093ba211720771d Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Fri, 27 Feb 2026 09:36:59 +0900
Subject: [PATCH v1 1/2] Fix two defects with extended statistics for
 expressions

This commit addresses two pointer dereferences that are reachable in the
extended statistics code:
1) When building the statistics, the examination of an expression
through examine_attribute() missed the fact that this routine could
return NULL if a typanalyze function triggers its give-up path
(typanalyze callback returns false, no rows or no stats computed).  The
build of statistics for MCV, dependencies, ndistinct and expressions
would have tried to look at a NULL pointer, crashing the server.
2) When loading extended statistics for expressions,
statext_expressions_load() has never considered the fact that an element
of the pg_statistic array, storing the statistics of an expression,
could store a NULL entry.  Such entries can be generated for example on
an expression whose statistics could not be gathered.

While these conditions cannot be reached with the in-core typanalyze
callbacks, it is possible to trigger these issues with at least a custom
data with a typanalyze callback.

Attribute statistics offer already similar protections than the ones
added in this commit.
---
 src/backend/statistics/extended_stats.c | 20 ++++++++++++++++++++
 src/backend/utils/adt/selfuncs.c        |  6 +++++-
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c
index 3895ed72ef75..334c64985814 100644
--- a/src/backend/statistics/extended_stats.c
+++ b/src/backend/statistics/extended_stats.c
@@ -736,6 +736,16 @@ lookup_var_attr_stats(Bitmapset *attrs, List *exprs,
 
 		stats[i] = examine_attribute(expr);
 
+		/*
+		 * If the expression has been found as non-analyzable, give up.  We
+		 * will not be able to build extended stats with it.
+		 */
+		if (stats[i] == NULL)
+		{
+			pfree(stats);
+			return NULL;
+		}
+
 		/*
 		 * XXX We need tuple descriptor later, and we just grab it from
 		 * stats[0]->tupDesc (see e.g. statext_mcv_build). But as coded
@@ -2396,6 +2406,9 @@ serialize_expr_stats(AnlExprData *exprdata, int nexprs)
 /*
  * Loads pg_statistic record from expression statistics for expression
  * identified by the supplied index.
+ *
+ * Returns the pg_statistic record found, or NULL if there is no statistics
+ * data to use.
  */
 HeapTuple
 statext_expressions_load(Oid stxoid, bool inh, int idx)
@@ -2424,6 +2437,13 @@ statext_expressions_load(Oid stxoid, bool inh, int idx)
 
 	deconstruct_expanded_array(eah);
 
+	if (eah->dnulls && eah->dnulls[idx])
+	{
+		/* No data found for this expression, give up. */
+		ReleaseSysCache(htup);
+		return NULL;
+	}
+
 	td = DatumGetHeapTupleHeader(eah->dvalues[idx]);
 
 	/* Build a temporary HeapTuple control structure */
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 29fec6555938..587edfc6e839 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5923,7 +5923,11 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
 					vardata->statsTuple =
 						statext_expressions_load(info->statOid, rte->inh, pos);
 
-					vardata->freefunc = ReleaseDummy;
+					/* Nothing to release if no data found */
+					if (vardata->statsTuple != NULL)
+					{
+						vardata->freefunc = ReleaseDummy;
+					}
 
 					/*
 					 * Test if user has permission to access all rows from the
-- 
2.53.0