From 7ba8c9eb441befd6c3a076e89dbc2897444afcac Mon Sep 17 00:00:00 2001
From: Sami Imseih <samimseih@gmail.com>
Date: Thu, 2 Jul 2026 16:15:05 -0500
Subject: [PATCH v1 1/1] Add validations for required callbacks during
 pgstat_register_kind()

pgstat_register_kind() did not validate that required callbacks were
set, which could lead to NULL pointer dereferences.

For fixed-amount kinds, init_shmem_cb, reset_all_cb, and snapshot_cb
are called unconditionally. A missing callback crashes the server at
startup or during recovery.

For variable-numbered kinds, flush_pending_cb is called unconditionally
when flushing pending entries. If pending_size is set but
flush_pending_cb is NULL, the server crashes when that callback is run.

Add validation checks at registration time for custom kinds.

Also, add asserts at the call sites for init_shmem_cb and reset_all_cb
to also catch issues in built-in kinds during development. The assert
already exists for snapshot_cb.
---
 src/backend/utils/activity/pgstat.c       | 17 +++++++++++++++++
 src/backend/utils/activity/pgstat_shmem.c |  1 +
 2 files changed, 18 insertions(+)

diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index c4fa14f138f..021a7131532 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -908,7 +908,10 @@ pgstat_reset_of_kind(PgStat_Kind kind)
 	TimestampTz ts = GetCurrentTimestamp();
 
 	if (kind_info->fixed_amount)
+	{
+		Assert(kind_info->reset_all_cb != NULL);
 		kind_info->reset_all_cb(ts);
+	}
 	else
 		pgstat_reset_entries_of_kind(kind, ts);
 }
@@ -1533,11 +1536,24 @@ pgstat_register_kind(PgStat_Kind kind, const PgStat_KindInfo *kind_info)
 			ereport(ERROR,
 					(errmsg("custom cumulative statistics property is invalid"),
 					 errhint("Custom cumulative statistics require a shared memory size for fixed-numbered objects.")));
+		if (kind_info->init_shmem_cb == NULL ||
+			kind_info->reset_all_cb == NULL ||
+			kind_info->snapshot_cb == NULL)
+			ereport(ERROR,
+					(errmsg("custom cumulative statistics property is invalid"),
+					 errhint("Custom cumulative statistics require init_shmem_cb, reset_all_cb, and snapshot_cb callbacks for fixed-numbered objects.")));
 		if (kind_info->track_entry_count)
 			ereport(ERROR,
 					(errmsg("custom cumulative statistics property is invalid"),
 					 errhint("Custom cumulative statistics cannot use entry count tracking for fixed-numbered objects.")));
 	}
+	else
+	{
+		if (kind_info->pending_size > 0 && kind_info->flush_pending_cb == NULL)
+			ereport(ERROR,
+					(errmsg("custom cumulative statistics property is invalid"),
+					 errhint("Custom cumulative statistics require a flush_pending_cb callback when pending_size is set.")));
+	}
 
 	/*
 	 * If pgstat_kind_custom_infos is not available yet, allocate it.
@@ -2132,6 +2148,7 @@ pgstat_reset_after_failure(void)
 		if (!kind_info || !kind_info->fixed_amount)
 			continue;
 
+		Assert(kind_info->reset_all_cb != NULL);
 		kind_info->reset_all_cb(ts);
 	}
 
diff --git a/src/backend/utils/activity/pgstat_shmem.c b/src/backend/utils/activity/pgstat_shmem.c
index 5ea3f1973f9..d00dbb90f16 100644
--- a/src/backend/utils/activity/pgstat_shmem.c
+++ b/src/backend/utils/activity/pgstat_shmem.c
@@ -247,6 +247,7 @@ StatsShmemInit(void *arg)
 				ptr = ctl->custom_data[idx];
 			}
 
+			Assert(kind_info->init_shmem_cb != NULL);
 			kind_info->init_shmem_cb(ptr);
 		}
 	}
-- 
2.50.1 (Apple Git-155)

