From 1b624f705547206cb7dea59f08b8db21fe38d041 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathandbossart@gmail.com>
Date: Sun, 10 Apr 2022 14:27:58 -0700
Subject: [PATCH v2 3/3] Block attempts to set GUCs while loading
 shared_preload_libraries.

This change attempts to block parameter changes in preloaded
libraries' _PG_init() functions.  We cannot reliably support such
behavior because certain parameters contribute to global values
we've already calculated at this point (e.g., MaxBackends).  We'd
rather make sure values like MaxBackends are available for use in
_PG_init(), and we encourage extension authors to instead recommend
suitable settings and error if such recommendations are not heeded.
Of course, preloaded libraries could still change the value
directly instead of via SetConfigOption(), but trying to handle
that is probably more trouble than it's worth.
---
 src/backend/utils/misc/guc.c | 51 +++++++++++++++++++++++++++++++++---
 1 file changed, 47 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9e0f262088..51a6e16fe0 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -161,6 +161,11 @@ char	   *GUC_check_errdetail_string;
 char	   *GUC_check_errhint_string;
 
 static void do_serialize(char **destptr, Size *maxbytes, const char *fmt,...) pg_attribute_printf(3, 4);
+static int set_config_option_internal(const char *name, const char *value,
+									  GucContext context, GucSource source,
+									  GucAction action, bool changeVal,
+									  int elevel, bool is_reload,
+									  bool allow_when_loading_preload_libs);
 
 static void set_config_sourcefile(const char *name, char *sourcefile,
 								  int sourceline);
@@ -7560,6 +7565,21 @@ set_config_option(const char *name, const char *value,
 				  GucContext context, GucSource source,
 				  GucAction action, bool changeVal, int elevel,
 				  bool is_reload)
+{
+	return set_config_option_internal(name, value, context, source, action,
+									  changeVal, elevel, is_reload, false);
+}
+
+/*
+ * Just like set_config_option(), except allows overriding the ERROR when called
+ * while loading shared_preload_libraries.  This is needed for defining custom
+ * GUCs, which involves calling this function to set the GUC.
+ */
+static int
+set_config_option_internal(const char *name, const char *value,
+						   GucContext context, GucSource source,
+						   GucAction action, bool changeVal, int elevel,
+						   bool is_reload, bool allow_when_loading_preload_libs)
 {
 	struct config_generic *record;
 	union config_var_val newval_union;
@@ -7567,6 +7587,28 @@ set_config_option(const char *name, const char *value,
 	bool		prohibitValueChange = false;
 	bool		makeDefault;
 
+	/*
+	 * We attempt to block parameter changes in preloaded libraries' _PG_init()
+	 * functions.  We cannot reliably support such behavior because certain
+	 * parameters contribute to global values we've already calculated at this
+	 * point (e.g., MaxBackends).  We'd rather make sure values like MaxBackends
+	 * are available for use in _PG_init(), and we encourage extension authors
+	 * to instead recommend suitable settings and error if such recommendations
+	 * are not heeded.  Of course, preloaded libraries could still change the
+	 * value directly instead of via SetConfigOption(), but trying to handle
+	 * that is probably more trouble than it's worth.
+	 *
+	 * This ERROR is bypassed when allow_when_loading_preload_libs is true.
+	 * This is needed for defining custom GUCs, which involves calling this
+	 * function to set the GUC.  We still want to allow that.
+	 */
+	if (process_shared_preload_libraries_in_progress &&
+		!allow_when_loading_preload_libs)
+		ereport(ERROR,
+				(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+				 errmsg("cannot change parameters while loading "
+						"\"shared_preload_libraries\"")));
+
 	if (elevel == 0)
 	{
 		if (source == PGC_S_DEFAULT || source == PGC_S_FILE)
@@ -9353,10 +9395,11 @@ define_custom_variable(struct config_generic *variable)
 
 	/* First, apply the reset value if any */
 	if (pHolder->reset_val)
-		(void) set_config_option(name, pHolder->reset_val,
-								 pHolder->gen.reset_scontext,
-								 pHolder->gen.reset_source,
-								 GUC_ACTION_SET, true, WARNING, false);
+		(void) set_config_option_internal(name, pHolder->reset_val,
+										  pHolder->gen.reset_scontext,
+										  pHolder->gen.reset_source,
+										  GUC_ACTION_SET, true, WARNING, false,
+										  true);
 	/* That should not have resulted in stacking anything */
 	Assert(variable->stack == NULL);
 
-- 
2.25.1

