commit 9f7b1ab64bdd054d2f6a61e3f64d7a5b94797647
Author: Tom Lane <tgl@sss.pgh.pa.us>
Date:   Tue Sep 13 11:49:40 2022 -0400

    Store GUC data in a memory context, not with malloc().
    
    The only real argument for using malloc directly was that we needed
    the ability to not throw error on OOM; but mcxt.c grew that feature
    awhile ago.
    
    Keeping the data in a memory context improves accountability and
    debuggability --- for example, without this it's almost impossible
    to detect memory leaks in the GUC code with anything less costly
    than valgrind.  Moreover, the next patch in this series will add a
    hash table for GUC lookup, and it'd be pretty silly to be using
    palloc-dependent hash facilities along with malloc storage of the
    underlying data.
    
    This is a bit invasive though, in particular causing an API break
    for GUC check hooks that want to modify the GUC's value or use an
    "extra" data structure.  My guess is that not very many extensions
    will be affected by that, but perhaps I'm wrong.
    
    One note is that this changes ParseLongOption() to return short-lived
    palloc'd not malloc'd data.  There wasn't any caller for which the
    latter was better.

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 58752368e7..ee54723c8d 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -287,8 +287,8 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
 					}
 
 					SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV);
-					free(name);
-					free(value);
+					pfree(name);
+					pfree(value);
 					break;
 				}
 			default:
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index b69ff37dbb..45b30ca566 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -1290,8 +1290,8 @@ check_temp_tablespaces(char **newval, void **extra, GucSource source)
 		}
 
 		/* Now prepare an "extra" struct for assign_temp_tablespaces */
-		myextra = malloc(offsetof(temp_tablespaces_extra, tblSpcs) +
-						 numSpcs * sizeof(Oid));
+		myextra = guc_malloc(LOG, offsetof(temp_tablespaces_extra, tblSpcs) +
+							 numSpcs * sizeof(Oid));
 		if (!myextra)
 			return false;
 		myextra->numSpcs = numSpcs;
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index c795cb7a29..32db05f132 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -148,7 +148,7 @@ check_datestyle(char **newval, void **extra, GucSource source)
 			char	   *subval;
 			void	   *subextra = NULL;
 
-			subval = strdup(GetConfigOptionResetString("datestyle"));
+			subval = guc_strdup(LOG, GetConfigOptionResetString("datestyle"));
 			if (!subval)
 			{
 				ok = false;
@@ -156,7 +156,7 @@ check_datestyle(char **newval, void **extra, GucSource source)
 			}
 			if (!check_datestyle(&subval, &subextra, source))
 			{
-				free(subval);
+				guc_free(subval);
 				ok = false;
 				break;
 			}
@@ -165,8 +165,8 @@ check_datestyle(char **newval, void **extra, GucSource source)
 				newDateStyle = myextra[0];
 			if (!have_order)
 				newDateOrder = myextra[1];
-			free(subval);
-			free(subextra);
+			guc_free(subval);
+			guc_free(subextra);
 		}
 		else
 		{
@@ -187,9 +187,9 @@ check_datestyle(char **newval, void **extra, GucSource source)
 	}
 
 	/*
-	 * Prepare the canonical string to return.  GUC wants it malloc'd.
+	 * Prepare the canonical string to return.  GUC wants it guc_malloc'd.
 	 */
-	result = (char *) malloc(32);
+	result = (char *) guc_malloc(LOG, 32);
 	if (!result)
 		return false;
 
@@ -221,13 +221,13 @@ check_datestyle(char **newval, void **extra, GucSource source)
 			break;
 	}
 
-	free(*newval);
+	guc_free(*newval);
 	*newval = result;
 
 	/*
 	 * Set up the "extra" struct actually used by assign_datestyle.
 	 */
-	myextra = (int *) malloc(2 * sizeof(int));
+	myextra = (int *) guc_malloc(LOG, 2 * sizeof(int));
 	if (!myextra)
 		return false;
 	myextra[0] = newDateStyle;
@@ -366,7 +366,7 @@ check_timezone(char **newval, void **extra, GucSource source)
 	/*
 	 * Pass back data for assign_timezone to use
 	 */
-	*extra = malloc(sizeof(pg_tz *));
+	*extra = guc_malloc(LOG, sizeof(pg_tz *));
 	if (!*extra)
 		return false;
 	*((pg_tz **) *extra) = new_tz;
@@ -439,7 +439,7 @@ check_log_timezone(char **newval, void **extra, GucSource source)
 	/*
 	 * Pass back data for assign_log_timezone to use
 	 */
-	*extra = malloc(sizeof(pg_tz *));
+	*extra = guc_malloc(LOG, sizeof(pg_tz *));
 	if (!*extra)
 		return false;
 	*((pg_tz **) *extra) = new_tz;
@@ -500,7 +500,7 @@ check_timezone_abbreviations(char **newval, void **extra, GucSource source)
 		return true;
 	}
 
-	/* OK, load the file and produce a malloc'd TimeZoneAbbrevTable */
+	/* OK, load the file and produce a guc_malloc'd TimeZoneAbbrevTable */
 	*extra = load_tzoffsets(*newval);
 
 	/* tzparser.c returns NULL on failure, reporting via GUC_check_errmsg */
@@ -647,7 +647,7 @@ check_transaction_deferrable(bool *newval, void **extra, GucSource source)
 bool
 check_random_seed(double *newval, void **extra, GucSource source)
 {
-	*extra = malloc(sizeof(int));
+	*extra = guc_malloc(LOG, sizeof(int));
 	if (!*extra)
 		return false;
 	/* Arm the assign only if source of value is an interactive SET */
@@ -735,8 +735,8 @@ check_client_encoding(char **newval, void **extra, GucSource source)
 	if (strcmp(*newval, canonical_name) != 0 &&
 		strcmp(*newval, "UNICODE") != 0)
 	{
-		free(*newval);
-		*newval = strdup(canonical_name);
+		guc_free(*newval);
+		*newval = guc_strdup(LOG, canonical_name);
 		if (!*newval)
 			return false;
 	}
@@ -744,7 +744,7 @@ check_client_encoding(char **newval, void **extra, GucSource source)
 	/*
 	 * Save the encoding's ID in *extra, for use by assign_client_encoding.
 	 */
-	*extra = malloc(sizeof(int));
+	*extra = guc_malloc(LOG, sizeof(int));
 	if (!*extra)
 		return false;
 	*((int *) *extra) = encoding;
@@ -847,7 +847,7 @@ check_session_authorization(char **newval, void **extra, GucSource source)
 	ReleaseSysCache(roleTup);
 
 	/* Set up "extra" struct for assign_session_authorization to use */
-	myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
+	myextra = (role_auth_extra *) guc_malloc(LOG, sizeof(role_auth_extra));
 	if (!myextra)
 		return false;
 	myextra->roleid = roleid;
@@ -957,7 +957,7 @@ check_role(char **newval, void **extra, GucSource source)
 	}
 
 	/* Set up "extra" struct for assign_role to use */
-	myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
+	myextra = (role_auth_extra *) guc_malloc(LOG, sizeof(role_auth_extra));
 	if (!myextra)
 		return false;
 	myextra->roleid = roleid;
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index de1184ad7b..b8a5ea4532 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -849,8 +849,8 @@ PostmasterMain(int argc, char *argv[])
 					}
 
 					SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV);
-					free(name);
-					free(value);
+					pfree(name);
+					pfree(value);
 					break;
 				}
 
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index e360d925b0..1a022b11a0 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -1054,9 +1054,9 @@ check_synchronous_standby_names(char **newval, void **extra, GucSource source)
 			return false;
 		}
 
-		/* GUC extra value must be malloc'd, not palloc'd */
+		/* GUC extra value must be guc_malloc'd, not palloc'd */
 		pconf = (SyncRepConfigData *)
-			malloc(syncrep_parse_result->config_size);
+			guc_malloc(LOG, syncrep_parse_result->config_size);
 		if (pconf == NULL)
 			return false;
 		memcpy(pconf, syncrep_parse_result, syncrep_parse_result->config_size);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 35eff28bd3..f59eb7a583 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3869,8 +3869,8 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx,
 											optarg)));
 					}
 					SetConfigOption(name, value, ctx, gucsource);
-					free(name);
-					free(value);
+					pfree(name);
+					pfree(value);
 					break;
 				}
 
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 43fff50d49..98a90a81c0 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -29,6 +29,7 @@
 #include "utils/builtins.h"
 #include "utils/date.h"
 #include "utils/datetime.h"
+#include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/tzparser.h"
 
@@ -4782,8 +4783,8 @@ TemporalSimplify(int32 max_precis, Node *node)
  * to create the final array of timezone tokens.  The argument array
  * is already sorted in name order.
  *
- * The result is a TimeZoneAbbrevTable (which must be a single malloc'd chunk)
- * or NULL on malloc failure.  No other error conditions are defined.
+ * The result is a TimeZoneAbbrevTable (which must be a single guc_malloc'd
+ * chunk) or NULL on alloc failure.  No other error conditions are defined.
  */
 TimeZoneAbbrevTable *
 ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
@@ -4812,7 +4813,7 @@ ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
 	}
 
 	/* Alloc the result ... */
-	tbl = malloc(tbl_size);
+	tbl = guc_malloc(LOG, tbl_size);
 	if (!tbl)
 		return NULL;
 
diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c
index 890832f353..450ea34336 100644
--- a/src/backend/utils/cache/ts_cache.c
+++ b/src/backend/utils/cache/ts_cache.c
@@ -633,9 +633,9 @@ check_default_text_search_config(char **newval, void **extra, GucSource source)
 
 		ReleaseSysCache(tuple);
 
-		/* GUC wants it malloc'd not palloc'd */
-		free(*newval);
-		*newval = strdup(buf);
+		/* GUC wants it guc_malloc'd not palloc'd */
+		guc_free(*newval);
+		*newval = guc_strdup(LOG, buf);
 		pfree(buf);
 		if (!*newval)
 			return false;
diff --git a/src/backend/utils/misc/README b/src/backend/utils/misc/README
index 6e294386f7..85d97d29b6 100644
--- a/src/backend/utils/misc/README
+++ b/src/backend/utils/misc/README
@@ -51,13 +51,13 @@ out-of-memory.
 This might be used for example to canonicalize the spelling of a string
 value, round off a buffer size to the nearest supported value, or replace
 a special value such as "-1" with a computed default value.  If the
-function wishes to replace a string value, it must malloc (not palloc)
-the replacement value, and be sure to free() the previous value.
+function wishes to replace a string value, it must guc_malloc (not palloc)
+the replacement value, and be sure to guc_free() the previous value.
 
 * Derived information, such as the role OID represented by a user name,
-can be stored for use by the assign hook.  To do this, malloc (not palloc)
+can be stored for use by the assign hook.  To do this, guc_malloc (not palloc)
 storage space for the information, and return its address at *extra.
-guc.c will automatically free() this space when the associated GUC setting
+guc.c will automatically guc_free() this space when the associated GUC setting
 is no longer of interest.  *extra is initialized to NULL before call, so
 it can be ignored if not needed.
 
@@ -255,10 +255,9 @@ maintained by GUC.
 GUC Memory Handling
 -------------------
 
-String variable values are allocated with malloc/strdup, not with the
-palloc/pstrdup mechanisms.  We would need to keep them in a permanent
-context anyway, and malloc gives us more control over handling
-out-of-memory failures.
+String variable values are allocated with guc_malloc or guc_strdup,
+which ensure that the values are kept in a long-lived context, and provide
+more control over handling out-of-memory failures than bare palloc.
 
 We allow a string variable's actual value, reset_val, boot_val, and stacked
 values to point at the same storage.  This makes it slightly harder to free
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index b11d609bbe..985979f140 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -188,6 +188,9 @@ static const char *const map_old_guc_names[] = {
 };
 
 
+/* Memory context holding all GUC-related data */
+static MemoryContext GUCMemoryContext;
+
 /*
  * Actual lookup of variables is done through this single, sorted array.
  */
@@ -595,19 +598,22 @@ bail_out:
 	return head;
 }
 
+
 /*
- * Some infrastructure for checking malloc/strdup/realloc calls
+ * Some infrastructure for GUC-related memory allocation
+ *
+ * These functions are generally modeled on libc's malloc/realloc/etc,
+ * but any OOM issue is reported at the specified elevel.
+ * (Thus, control returns only if that's less than ERROR.)
  */
 void *
 guc_malloc(int elevel, size_t size)
 {
 	void	   *data;
 
-	/* Avoid unportable behavior of malloc(0) */
-	if (size == 0)
-		size = 1;
-	data = malloc(size);
-	if (data == NULL)
+	data = MemoryContextAllocExtended(GUCMemoryContext, size,
+									  MCXT_ALLOC_NO_OOM);
+	if (unlikely(data == NULL))
 		ereport(elevel,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of memory")));
@@ -619,11 +625,13 @@ guc_realloc(int elevel, void *old, size_t size)
 {
 	void	   *data;
 
-	/* Avoid unportable behavior of realloc(NULL, 0) */
-	if (old == NULL && size == 0)
-		size = 1;
-	data = realloc(old, size);
-	if (data == NULL)
+	if (old != NULL)
+		data = repalloc_extended(old, size,
+								 MCXT_ALLOC_NO_OOM);
+	else
+		data = MemoryContextAllocExtended(GUCMemoryContext, size,
+										  MCXT_ALLOC_NO_OOM);
+	if (unlikely(data == NULL))
 		ereport(elevel,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of memory")));
@@ -634,15 +642,25 @@ char *
 guc_strdup(int elevel, const char *src)
 {
 	char	   *data;
+	size_t		len = strlen(src) + 1;
 
-	data = strdup(src);
-	if (data == NULL)
-		ereport(elevel,
-				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("out of memory")));
+	data = guc_malloc(elevel, len);
+	if (likely(data != NULL))
+		memcpy(data, src, len);
 	return data;
 }
 
+void
+guc_free(void *ptr)
+{
+	/*
+	 * Historically, GUC-related code has relied heavily on the ability to do
+	 * free(NULL), so we allow that here even though pfree() doesn't.
+	 */
+	if (ptr != NULL)
+		pfree(ptr);
+}
+
 
 /*
  * Detect whether strval is referenced anywhere in a GUC string item
@@ -680,7 +698,7 @@ set_string_field(struct config_string *conf, char **field, char *newval)
 
 	/* Free old value if it's not NULL and isn't referenced anymore */
 	if (oldval && !string_field_used(conf, oldval))
-		free(oldval);
+		guc_free(oldval);
 }
 
 /*
@@ -741,7 +759,7 @@ set_extra_field(struct config_generic *gconf, void **field, void *newval)
 
 	/* Free old value if it's not NULL and isn't referenced anymore */
 	if (oldval && !extra_field_used(gconf, oldval))
-		free(oldval);
+		guc_free(oldval);
 }
 
 /*
@@ -749,7 +767,7 @@ set_extra_field(struct config_generic *gconf, void **field, void *newval)
  * The "extra" field associated with the active value is copied, too.
  *
  * NB: be sure stringval and extra fields of a new stack entry are
- * initialized to NULL before this is used, else we'll try to free() them.
+ * initialized to NULL before this is used, else we'll try to guc_free() them.
  */
 static void
 set_stack_value(struct config_generic *gconf, config_var_value *val)
@@ -817,9 +835,9 @@ get_guc_variables(void)
 
 
 /*
- * Build the sorted array.  This is split out so that it could be
- * re-executed after startup (e.g., we could allow loadable modules to
- * add vars, and then we'd need to re-sort).
+ * Build the sorted array.  This is split out so that help_config.c can
+ * extract all the variables without running all of InitializeGUCOptions.
+ * It's not meant for use anyplace else.
  */
 void
 build_guc_variables(void)
@@ -829,6 +847,17 @@ build_guc_variables(void)
 	struct config_generic **guc_vars;
 	int			i;
 
+	/*
+	 * Create the memory context that will hold all GUC-related data.
+	 */
+	Assert(GUCMemoryContext == NULL);
+	GUCMemoryContext = AllocSetContextCreate(TopMemoryContext,
+											 "GUCMemoryContext",
+											 ALLOCSET_DEFAULT_SIZES);
+
+	/*
+	 * Count all the built-in variables, and set their vartypes correctly.
+	 */
 	for (i = 0; ConfigureNamesBool[i].gen.name; i++)
 	{
 		struct config_bool *conf = &ConfigureNamesBool[i];
@@ -895,7 +924,7 @@ build_guc_variables(void)
 	for (i = 0; ConfigureNamesEnum[i].gen.name; i++)
 		guc_vars[num_vars++] = &ConfigureNamesEnum[i].gen;
 
-	free(guc_variables);
+	guc_free(guc_variables);
 	guc_variables = guc_vars;
 	num_guc_variables = num_vars;
 	size_guc_variables = size_vars;
@@ -1001,7 +1030,7 @@ add_placeholder_variable(const char *name, int elevel)
 	gen->name = guc_strdup(elevel, name);
 	if (gen->name == NULL)
 	{
-		free(var);
+		guc_free(var);
 		return NULL;
 	}
 
@@ -1020,8 +1049,8 @@ add_placeholder_variable(const char *name, int elevel)
 
 	if (!add_guc_variable((struct config_generic *) var, elevel))
 	{
-		free(unconstify(char *, gen->name));
-		free(var);
+		guc_free(unconstify(char *, gen->name));
+		guc_free(var);
 		return NULL;
 	}
 
@@ -1255,7 +1284,7 @@ InitializeGUCOptions(void)
 	pg_timezone_initialize();
 
 	/*
-	 * Build sorted array of all GUC variables.
+	 * Create GUCMemoryContext and build sorted array of all GUC variables.
 	 */
 	build_guc_variables();
 
@@ -1482,6 +1511,7 @@ SelectConfigFiles(const char *userDoption, const char *progname)
 {
 	char	   *configdir;
 	char	   *fname;
+	bool		fname_is_malloced;
 	struct stat stat_buf;
 	struct config_string *data_directory_rec;
 
@@ -1509,12 +1539,16 @@ SelectConfigFiles(const char *userDoption, const char *progname)
 	 * the same way by future backends.
 	 */
 	if (ConfigFileName)
+	{
 		fname = make_absolute_path(ConfigFileName);
+		fname_is_malloced = true;
+	}
 	else if (configdir)
 	{
 		fname = guc_malloc(FATAL,
 						   strlen(configdir) + strlen(CONFIG_FILENAME) + 2);
 		sprintf(fname, "%s/%s", configdir, CONFIG_FILENAME);
+		fname_is_malloced = false;
 	}
 	else
 	{
@@ -1530,7 +1564,11 @@ SelectConfigFiles(const char *userDoption, const char *progname)
 	 * it can't be overridden later.
 	 */
 	SetConfigOption("config_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
-	free(fname);
+
+	if (fname_is_malloced)
+		free(fname);
+	else
+		guc_free(fname);
 
 	/*
 	 * Now read the config file for the first time.
@@ -1604,12 +1642,16 @@ SelectConfigFiles(const char *userDoption, const char *progname)
 	 * Figure out where pg_hba.conf is, and make sure the path is absolute.
 	 */
 	if (HbaFileName)
+	{
 		fname = make_absolute_path(HbaFileName);
+		fname_is_malloced = true;
+	}
 	else if (configdir)
 	{
 		fname = guc_malloc(FATAL,
 						   strlen(configdir) + strlen(HBA_FILENAME) + 2);
 		sprintf(fname, "%s/%s", configdir, HBA_FILENAME);
+		fname_is_malloced = false;
 	}
 	else
 	{
@@ -1621,18 +1663,26 @@ SelectConfigFiles(const char *userDoption, const char *progname)
 		return false;
 	}
 	SetConfigOption("hba_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
-	free(fname);
+
+	if (fname_is_malloced)
+		free(fname);
+	else
+		guc_free(fname);
 
 	/*
 	 * Likewise for pg_ident.conf.
 	 */
 	if (IdentFileName)
+	{
 		fname = make_absolute_path(IdentFileName);
+		fname_is_malloced = true;
+	}
 	else if (configdir)
 	{
 		fname = guc_malloc(FATAL,
 						   strlen(configdir) + strlen(IDENT_FILENAME) + 2);
 		sprintf(fname, "%s/%s", configdir, IDENT_FILENAME);
+		fname_is_malloced = false;
 	}
 	else
 	{
@@ -1644,7 +1694,11 @@ SelectConfigFiles(const char *userDoption, const char *progname)
 		return false;
 	}
 	SetConfigOption("ident_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
-	free(fname);
+
+	if (fname_is_malloced)
+		free(fname);
+	else
+		guc_free(fname);
 
 	free(configdir);
 
@@ -2289,12 +2343,12 @@ ReportGUCOption(struct config_generic *record)
 		pq_endmessage(&msgbuf);
 
 		/*
-		 * We need a long-lifespan copy.  If strdup() fails due to OOM, we'll
-		 * set last_reported to NULL and thereby possibly make a duplicate
-		 * report later.
+		 * We need a long-lifespan copy.  If guc_strdup() fails due to OOM,
+		 * we'll set last_reported to NULL and thereby possibly make a
+		 * duplicate report later.
 		 */
-		free(record->last_reported);
-		record->last_reported = strdup(val);
+		guc_free(record->last_reported);
+		record->last_reported = guc_strdup(LOG, val);
 	}
 
 	pfree(val);
@@ -2893,7 +2947,7 @@ parse_and_validate_value(struct config_generic *record,
 				if (!call_string_check_hook(conf, &newval->stringval, newextra,
 											source, elevel))
 				{
-					free(newval->stringval);
+					guc_free(newval->stringval);
 					newval->stringval = NULL;
 					return false;
 				}
@@ -3308,7 +3362,7 @@ set_config_option_ext(const char *name, const char *value,
 				{
 					/* Release newextra, unless it's reset_extra */
 					if (newextra && !extra_field_used(&conf->gen, newextra))
-						free(newextra);
+						guc_free(newextra);
 
 					if (*conf->variable != newval)
 					{
@@ -3367,7 +3421,7 @@ set_config_option_ext(const char *name, const char *value,
 
 				/* Perhaps we didn't install newextra anywhere */
 				if (newextra && !extra_field_used(&conf->gen, newextra))
-					free(newextra);
+					guc_free(newextra);
 				break;
 
 #undef newval
@@ -3406,7 +3460,7 @@ set_config_option_ext(const char *name, const char *value,
 				{
 					/* Release newextra, unless it's reset_extra */
 					if (newextra && !extra_field_used(&conf->gen, newextra))
-						free(newextra);
+						guc_free(newextra);
 
 					if (*conf->variable != newval)
 					{
@@ -3465,7 +3519,7 @@ set_config_option_ext(const char *name, const char *value,
 
 				/* Perhaps we didn't install newextra anywhere */
 				if (newextra && !extra_field_used(&conf->gen, newextra))
-					free(newextra);
+					guc_free(newextra);
 				break;
 
 #undef newval
@@ -3504,7 +3558,7 @@ set_config_option_ext(const char *name, const char *value,
 				{
 					/* Release newextra, unless it's reset_extra */
 					if (newextra && !extra_field_used(&conf->gen, newextra))
-						free(newextra);
+						guc_free(newextra);
 
 					if (*conf->variable != newval)
 					{
@@ -3563,7 +3617,7 @@ set_config_option_ext(const char *name, const char *value,
 
 				/* Perhaps we didn't install newextra anywhere */
 				if (newextra && !extra_field_used(&conf->gen, newextra))
-					free(newextra);
+					guc_free(newextra);
 				break;
 
 #undef newval
@@ -3597,7 +3651,7 @@ set_config_option_ext(const char *name, const char *value,
 					if (!call_string_check_hook(conf, &newval, &newextra,
 												source, elevel))
 					{
-						free(newval);
+						guc_free(newval);
 						return 0;
 					}
 				}
@@ -3625,10 +3679,10 @@ set_config_option_ext(const char *name, const char *value,
 
 					/* Release newval, unless it's reset_val */
 					if (newval && !string_field_used(conf, newval))
-						free(newval);
+						guc_free(newval);
 					/* Release newextra, unless it's reset_extra */
 					if (newextra && !extra_field_used(&conf->gen, newextra))
-						free(newextra);
+						guc_free(newextra);
 
 					if (newval_different)
 					{
@@ -3689,10 +3743,10 @@ set_config_option_ext(const char *name, const char *value,
 
 				/* Perhaps we didn't install newval anywhere */
 				if (newval && !string_field_used(conf, newval))
-					free(newval);
+					guc_free(newval);
 				/* Perhaps we didn't install newextra anywhere */
 				if (newextra && !extra_field_used(&conf->gen, newextra))
-					free(newextra);
+					guc_free(newextra);
 				break;
 
 #undef newval
@@ -3731,7 +3785,7 @@ set_config_option_ext(const char *name, const char *value,
 				{
 					/* Release newextra, unless it's reset_extra */
 					if (newextra && !extra_field_used(&conf->gen, newextra))
-						free(newextra);
+						guc_free(newextra);
 
 					if (*conf->variable != newval)
 					{
@@ -3790,7 +3844,7 @@ set_config_option_ext(const char *name, const char *value,
 
 				/* Perhaps we didn't install newextra anywhere */
 				if (newextra && !extra_field_used(&conf->gen, newextra))
-					free(newextra);
+					guc_free(newextra);
 				break;
 
 #undef newval
@@ -3828,7 +3882,7 @@ set_config_sourcefile(const char *name, char *sourcefile, int sourceline)
 		return;
 
 	sourcefile = guc_strdup(elevel, sourcefile);
-	free(record->sourcefile);
+	guc_free(record->sourcefile);
 	record->sourcefile = sourcefile;
 	record->sourceline = sourceline;
 }
@@ -4219,8 +4273,8 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
 								name, value)));
 
 			if (record->vartype == PGC_STRING && newval.stringval != NULL)
-				free(newval.stringval);
-			free(newextra);
+				guc_free(newval.stringval);
+			guc_free(newextra);
 
 			/*
 			 * We must also reject values containing newlines, because the
@@ -4515,7 +4569,7 @@ define_custom_variable(struct config_generic *variable)
 	set_string_field(pHolder, pHolder->variable, NULL);
 	set_string_field(pHolder, &pHolder->reset_val, NULL);
 
-	free(pHolder);
+	guc_free(pHolder);
 }
 
 /*
@@ -4794,7 +4848,7 @@ MarkGUCPrefixReserved(const char *className)
 	}
 
 	/* And remember the name so we can prevent future mistakes. */
-	oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+	oldcontext = MemoryContextSwitchTo(GUCMemoryContext);
 	reserved_class_prefix = lappend(reserved_class_prefix, pstrdup(className));
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -5269,9 +5323,9 @@ read_nondefault_variables(void)
 		if (varsourcefile[0])
 			set_config_sourcefile(varname, varsourcefile, varsourceline);
 
-		free(varname);
-		free(varvalue);
-		free(varsourcefile);
+		guc_free(varname);
+		guc_free(varvalue);
+		guc_free(varsourcefile);
 	}
 
 	FreeFile(fp);
@@ -5713,9 +5767,9 @@ RestoreGUCState(void *gucstate)
 		 * pointers.
 		 */
 		Assert(gconf->stack == NULL);
-		free(gconf->extra);
-		free(gconf->last_reported);
-		free(gconf->sourcefile);
+		guc_free(gconf->extra);
+		guc_free(gconf->last_reported);
+		guc_free(gconf->sourcefile);
 		switch (gconf->vartype)
 		{
 			case PGC_BOOL:
@@ -5723,7 +5777,7 @@ RestoreGUCState(void *gucstate)
 					struct config_bool *conf = (struct config_bool *) gconf;
 
 					if (conf->reset_extra && conf->reset_extra != gconf->extra)
-						free(conf->reset_extra);
+						guc_free(conf->reset_extra);
 					break;
 				}
 			case PGC_INT:
@@ -5731,7 +5785,7 @@ RestoreGUCState(void *gucstate)
 					struct config_int *conf = (struct config_int *) gconf;
 
 					if (conf->reset_extra && conf->reset_extra != gconf->extra)
-						free(conf->reset_extra);
+						guc_free(conf->reset_extra);
 					break;
 				}
 			case PGC_REAL:
@@ -5739,18 +5793,18 @@ RestoreGUCState(void *gucstate)
 					struct config_real *conf = (struct config_real *) gconf;
 
 					if (conf->reset_extra && conf->reset_extra != gconf->extra)
-						free(conf->reset_extra);
+						guc_free(conf->reset_extra);
 					break;
 				}
 			case PGC_STRING:
 				{
 					struct config_string *conf = (struct config_string *) gconf;
 
-					free(*conf->variable);
+					guc_free(*conf->variable);
 					if (conf->reset_val && conf->reset_val != *conf->variable)
-						free(conf->reset_val);
+						guc_free(conf->reset_val);
 					if (conf->reset_extra && conf->reset_extra != gconf->extra)
-						free(conf->reset_extra);
+						guc_free(conf->reset_extra);
 					break;
 				}
 			case PGC_ENUM:
@@ -5758,7 +5812,7 @@ RestoreGUCState(void *gucstate)
 					struct config_enum *conf = (struct config_enum *) gconf;
 
 					if (conf->reset_extra && conf->reset_extra != gconf->extra)
-						free(conf->reset_extra);
+						guc_free(conf->reset_extra);
 					break;
 				}
 		}
@@ -5820,7 +5874,7 @@ RestoreGUCState(void *gucstate)
 /*
  * A little "long argument" simulation, although not quite GNU
  * compliant. Takes a string of the form "some-option=some value" and
- * returns name = "some_option" and value = "some value" in malloc'ed
+ * returns name = "some_option" and value = "some value" in palloc'ed
  * storage. Note that '-' is converted to '_' in the option name. If
  * there is no '=' in the input string then value will be NULL.
  */
@@ -5838,15 +5892,15 @@ ParseLongOption(const char *string, char **name, char **value)
 
 	if (string[equal_pos] == '=')
 	{
-		*name = guc_malloc(FATAL, equal_pos + 1);
+		*name = palloc(equal_pos + 1);
 		strlcpy(*name, string, equal_pos + 1);
 
-		*value = guc_strdup(FATAL, &string[equal_pos + 1]);
+		*value = pstrdup(&string[equal_pos + 1]);
 	}
 	else
 	{
 		/* no equal sign in string */
-		*name = guc_strdup(FATAL, string);
+		*name = pstrdup(string);
 		*value = NULL;
 	}
 
@@ -5880,8 +5934,6 @@ ProcessGUCArray(ArrayType *array,
 		char	   *s;
 		char	   *name;
 		char	   *value;
-		char	   *namecopy;
-		char	   *valuecopy;
 
 		d = array_ref(array, 1, &i,
 					  -1 /* varlenarray */ ,
@@ -5902,22 +5954,16 @@ ProcessGUCArray(ArrayType *array,
 					(errcode(ERRCODE_SYNTAX_ERROR),
 					 errmsg("could not parse setting for parameter \"%s\"",
 							name)));
-			free(name);
+			pfree(name);
 			continue;
 		}
 
-		/* free malloc'd strings immediately to avoid leak upon error */
-		namecopy = pstrdup(name);
-		free(name);
-		valuecopy = pstrdup(value);
-		free(value);
-
-		(void) set_config_option(namecopy, valuecopy,
+		(void) set_config_option(name, value,
 								 context, source,
 								 action, true, 0, false);
 
-		pfree(namecopy);
-		pfree(valuecopy);
+		pfree(name);
+		pfree(value);
 		pfree(s);
 	}
 }
@@ -6381,7 +6427,7 @@ call_string_check_hook(struct config_string *conf, char **newval, void **extra,
 	}
 	PG_CATCH();
 	{
-		free(*newval);
+		guc_free(*newval);
 		PG_RE_THROW();
 	}
 	PG_END_TRY();
diff --git a/src/backend/utils/misc/tzparser.c b/src/backend/utils/misc/tzparser.c
index 8f2c95f055..e291cb63b0 100644
--- a/src/backend/utils/misc/tzparser.c
+++ b/src/backend/utils/misc/tzparser.c
@@ -439,7 +439,7 @@ ParseTzFile(const char *filename, int depth,
  * load_tzoffsets --- read and parse the specified timezone offset file
  *
  * On success, return a filled-in TimeZoneAbbrevTable, which must have been
- * malloc'd not palloc'd.  On failure, return NULL, using GUC_check_errmsg
+ * guc_malloc'd not palloc'd.  On failure, return NULL, using GUC_check_errmsg
  * and friends to give details of the problem.
  */
 TimeZoneAbbrevTable *
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index e426dd757d..47d024ab5a 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -405,6 +405,7 @@ extern ArrayType *GUCArrayReset(ArrayType *array);
 extern void *guc_malloc(int elevel, size_t size);
 extern pg_nodiscard void *guc_realloc(int elevel, void *old, size_t size);
 extern char *guc_strdup(int elevel, const char *src);
+extern void guc_free(void *ptr);
 
 #ifdef EXEC_BACKEND
 extern void write_nondefault_variables(GucContext context);
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 190d286f1c..5dc334b61b 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -114,7 +114,7 @@ plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source)
 		list_free(elemlist);
 	}
 
-	myextra = (int *) malloc(sizeof(int));
+	myextra = (int *) guc_malloc(LOG, sizeof(int));
 	if (!myextra)
 		return false;
 	*myextra = extrachecks;
