From 69e219ef17c5100911851dcba093ba211720771d Mon Sep 17 00:00:00 2001 From: Michael Paquier 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