Store GUC data in a memory context, instead of using 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 alongside malloc'd 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.  They must now use guc_malloc() and
guc_free() instead of malloc() and free().  Failure to change
affected code will result in assertion failures or worse; but
thanks to recent effort in the mcxt infrastructure, it shouldn't
be too hard to diagnose such oversights (at least in assert-enabled
builds).

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
previous definition was better.

Discussion: https://postgr.es/m/2982579.1662416866@sss.pgh.pa.us
This commit is contained in:
Tom Lane 2022-10-14 12:10:48 -04:00
parent 9c911ec065
commit 407b50f2d4
13 changed files with 185 additions and 127 deletions

View File

@ -287,8 +287,8 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
} }
SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV); SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV);
free(name); pfree(name);
free(value); pfree(value);
break; break;
} }
default: default:

View File

@ -1290,7 +1290,7 @@ check_temp_tablespaces(char **newval, void **extra, GucSource source)
} }
/* Now prepare an "extra" struct for assign_temp_tablespaces */ /* Now prepare an "extra" struct for assign_temp_tablespaces */
myextra = malloc(offsetof(temp_tablespaces_extra, tblSpcs) + myextra = guc_malloc(LOG, offsetof(temp_tablespaces_extra, tblSpcs) +
numSpcs * sizeof(Oid)); numSpcs * sizeof(Oid));
if (!myextra) if (!myextra)
return false; return false;

View File

@ -148,7 +148,7 @@ check_datestyle(char **newval, void **extra, GucSource source)
char *subval; char *subval;
void *subextra = NULL; void *subextra = NULL;
subval = strdup(GetConfigOptionResetString("datestyle")); subval = guc_strdup(LOG, GetConfigOptionResetString("datestyle"));
if (!subval) if (!subval)
{ {
ok = false; ok = false;
@ -156,7 +156,7 @@ check_datestyle(char **newval, void **extra, GucSource source)
} }
if (!check_datestyle(&subval, &subextra, source)) if (!check_datestyle(&subval, &subextra, source))
{ {
free(subval); guc_free(subval);
ok = false; ok = false;
break; break;
} }
@ -165,8 +165,8 @@ check_datestyle(char **newval, void **extra, GucSource source)
newDateStyle = myextra[0]; newDateStyle = myextra[0];
if (!have_order) if (!have_order)
newDateOrder = myextra[1]; newDateOrder = myextra[1];
free(subval); guc_free(subval);
free(subextra); guc_free(subextra);
} }
else 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) if (!result)
return false; return false;
@ -221,13 +221,13 @@ check_datestyle(char **newval, void **extra, GucSource source)
break; break;
} }
free(*newval); guc_free(*newval);
*newval = result; *newval = result;
/* /*
* Set up the "extra" struct actually used by assign_datestyle. * 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) if (!myextra)
return false; return false;
myextra[0] = newDateStyle; myextra[0] = newDateStyle;
@ -366,7 +366,7 @@ check_timezone(char **newval, void **extra, GucSource source)
/* /*
* Pass back data for assign_timezone to use * Pass back data for assign_timezone to use
*/ */
*extra = malloc(sizeof(pg_tz *)); *extra = guc_malloc(LOG, sizeof(pg_tz *));
if (!*extra) if (!*extra)
return false; return false;
*((pg_tz **) *extra) = new_tz; *((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 * Pass back data for assign_log_timezone to use
*/ */
*extra = malloc(sizeof(pg_tz *)); *extra = guc_malloc(LOG, sizeof(pg_tz *));
if (!*extra) if (!*extra)
return false; return false;
*((pg_tz **) *extra) = new_tz; *((pg_tz **) *extra) = new_tz;
@ -500,7 +500,7 @@ check_timezone_abbreviations(char **newval, void **extra, GucSource source)
return true; 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); *extra = load_tzoffsets(*newval);
/* tzparser.c returns NULL on failure, reporting via GUC_check_errmsg */ /* 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 bool
check_random_seed(double *newval, void **extra, GucSource source) check_random_seed(double *newval, void **extra, GucSource source)
{ {
*extra = malloc(sizeof(int)); *extra = guc_malloc(LOG, sizeof(int));
if (!*extra) if (!*extra)
return false; return false;
/* Arm the assign only if source of value is an interactive SET */ /* 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 && if (strcmp(*newval, canonical_name) != 0 &&
strcmp(*newval, "UNICODE") != 0) strcmp(*newval, "UNICODE") != 0)
{ {
free(*newval); guc_free(*newval);
*newval = strdup(canonical_name); *newval = guc_strdup(LOG, canonical_name);
if (!*newval) if (!*newval)
return false; 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. * 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) if (!*extra)
return false; return false;
*((int *) *extra) = encoding; *((int *) *extra) = encoding;
@ -847,7 +847,7 @@ check_session_authorization(char **newval, void **extra, GucSource source)
ReleaseSysCache(roleTup); ReleaseSysCache(roleTup);
/* Set up "extra" struct for assign_session_authorization to use */ /* 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) if (!myextra)
return false; return false;
myextra->roleid = roleid; myextra->roleid = roleid;
@ -957,7 +957,7 @@ check_role(char **newval, void **extra, GucSource source)
} }
/* Set up "extra" struct for assign_role to use */ /* 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) if (!myextra)
return false; return false;
myextra->roleid = roleid; myextra->roleid = roleid;

View File

@ -849,8 +849,8 @@ PostmasterMain(int argc, char *argv[])
} }
SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV); SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV);
free(name); pfree(name);
free(value); pfree(value);
break; break;
} }

View File

@ -1054,9 +1054,9 @@ check_synchronous_standby_names(char **newval, void **extra, GucSource source)
return false; 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 *) pconf = (SyncRepConfigData *)
malloc(syncrep_parse_result->config_size); guc_malloc(LOG, syncrep_parse_result->config_size);
if (pconf == NULL) if (pconf == NULL)
return false; return false;
memcpy(pconf, syncrep_parse_result, syncrep_parse_result->config_size); memcpy(pconf, syncrep_parse_result, syncrep_parse_result->config_size);

View File

@ -3871,8 +3871,8 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx,
optarg))); optarg)));
} }
SetConfigOption(name, value, ctx, gucsource); SetConfigOption(name, value, ctx, gucsource);
free(name); pfree(name);
free(value); pfree(value);
break; break;
} }

View File

@ -29,6 +29,7 @@
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/date.h" #include "utils/date.h"
#include "utils/datetime.h" #include "utils/datetime.h"
#include "utils/guc.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/tzparser.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 * to create the final array of timezone tokens. The argument array
* is already sorted in name order. * is already sorted in name order.
* *
* The result is a TimeZoneAbbrevTable (which must be a single malloc'd chunk) * The result is a TimeZoneAbbrevTable (which must be a single guc_malloc'd
* or NULL on malloc failure. No other error conditions are defined. * chunk) or NULL on alloc failure. No other error conditions are defined.
*/ */
TimeZoneAbbrevTable * TimeZoneAbbrevTable *
ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n) ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
@ -4812,7 +4813,7 @@ ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
} }
/* Alloc the result ... */ /* Alloc the result ... */
tbl = malloc(tbl_size); tbl = guc_malloc(LOG, tbl_size);
if (!tbl) if (!tbl)
return NULL; return NULL;

View File

@ -633,9 +633,9 @@ check_default_text_search_config(char **newval, void **extra, GucSource source)
ReleaseSysCache(tuple); ReleaseSysCache(tuple);
/* GUC wants it malloc'd not palloc'd */ /* GUC wants it guc_malloc'd not palloc'd */
free(*newval); guc_free(*newval);
*newval = strdup(buf); *newval = guc_strdup(LOG, buf);
pfree(buf); pfree(buf);
if (!*newval) if (!*newval)
return false; return false;

View File

@ -51,13 +51,13 @@ out-of-memory.
This might be used for example to canonicalize the spelling of a string 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 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 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) function wishes to replace a string value, it must guc_malloc (not palloc)
the replacement value, and be sure to free() the previous value. the replacement value, and be sure to guc_free() the previous value.
* Derived information, such as the role OID represented by a user name, * 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. 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 is no longer of interest. *extra is initialized to NULL before call, so
it can be ignored if not needed. it can be ignored if not needed.
@ -255,10 +255,9 @@ maintained by GUC.
GUC Memory Handling GUC Memory Handling
------------------- -------------------
String variable values are allocated with malloc/strdup, not with the String variable values are allocated with guc_malloc or guc_strdup,
palloc/pstrdup mechanisms. We would need to keep them in a permanent which ensure that the values are kept in a long-lived context, and provide
context anyway, and malloc gives us more control over handling more control over handling out-of-memory failures than bare palloc.
out-of-memory failures.
We allow a string variable's actual value, reset_val, boot_val, and stacked 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 values to point at the same storage. This makes it slightly harder to free

View File

@ -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. * Actual lookup of variables is done through this single, sorted array.
*/ */
@ -595,19 +598,22 @@ bail_out:
return head; 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 * void *
guc_malloc(int elevel, size_t size) guc_malloc(int elevel, size_t size)
{ {
void *data; void *data;
/* Avoid unportable behavior of malloc(0) */ data = MemoryContextAllocExtended(GUCMemoryContext, size,
if (size == 0) MCXT_ALLOC_NO_OOM);
size = 1; if (unlikely(data == NULL))
data = malloc(size);
if (data == NULL)
ereport(elevel, ereport(elevel,
(errcode(ERRCODE_OUT_OF_MEMORY), (errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory"))); errmsg("out of memory")));
@ -619,11 +625,20 @@ guc_realloc(int elevel, void *old, size_t size)
{ {
void *data; void *data;
/* Avoid unportable behavior of realloc(NULL, 0) */ if (old != NULL)
if (old == NULL && size == 0) {
size = 1; /* This is to help catch old code that malloc's GUC data. */
data = realloc(old, size); Assert(GetMemoryChunkContext(old) == GUCMemoryContext);
if (data == NULL) data = repalloc_extended(old, size,
MCXT_ALLOC_NO_OOM);
}
else
{
/* Like realloc(3), but not like repalloc(), we allow old == NULL. */
data = MemoryContextAllocExtended(GUCMemoryContext, size,
MCXT_ALLOC_NO_OOM);
}
if (unlikely(data == NULL))
ereport(elevel, ereport(elevel,
(errcode(ERRCODE_OUT_OF_MEMORY), (errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory"))); errmsg("out of memory")));
@ -634,15 +649,29 @@ char *
guc_strdup(int elevel, const char *src) guc_strdup(int elevel, const char *src)
{ {
char *data; char *data;
size_t len = strlen(src) + 1;
data = strdup(src); data = guc_malloc(elevel, len);
if (data == NULL) if (likely(data != NULL))
ereport(elevel, memcpy(data, src, len);
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
return data; 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)
{
/* This is to help catch old code that malloc's GUC data. */
Assert(GetMemoryChunkContext(ptr) == GUCMemoryContext);
pfree(ptr);
}
}
/* /*
* Detect whether strval is referenced anywhere in a GUC string item * Detect whether strval is referenced anywhere in a GUC string item
@ -680,7 +709,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 */ /* Free old value if it's not NULL and isn't referenced anymore */
if (oldval && !string_field_used(conf, oldval)) if (oldval && !string_field_used(conf, oldval))
free(oldval); guc_free(oldval);
} }
/* /*
@ -741,7 +770,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 */ /* Free old value if it's not NULL and isn't referenced anymore */
if (oldval && !extra_field_used(gconf, oldval)) if (oldval && !extra_field_used(gconf, oldval))
free(oldval); guc_free(oldval);
} }
/* /*
@ -749,7 +778,7 @@ set_extra_field(struct config_generic *gconf, void **field, void *newval)
* The "extra" field associated with the active value is copied, too. * The "extra" field associated with the active value is copied, too.
* *
* NB: be sure stringval and extra fields of a new stack entry are * 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 static void
set_stack_value(struct config_generic *gconf, config_var_value *val) set_stack_value(struct config_generic *gconf, config_var_value *val)
@ -817,9 +846,9 @@ get_guc_variables(void)
/* /*
* Build the sorted array. This is split out so that it could be * Build the sorted array. This is split out so that help_config.c can
* re-executed after startup (e.g., we could allow loadable modules to * extract all the variables without running all of InitializeGUCOptions.
* add vars, and then we'd need to re-sort). * It's not meant for use anyplace else.
*/ */
void void
build_guc_variables(void) build_guc_variables(void)
@ -829,6 +858,17 @@ build_guc_variables(void)
struct config_generic **guc_vars; struct config_generic **guc_vars;
int i; 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++) for (i = 0; ConfigureNamesBool[i].gen.name; i++)
{ {
struct config_bool *conf = &ConfigureNamesBool[i]; struct config_bool *conf = &ConfigureNamesBool[i];
@ -895,7 +935,7 @@ build_guc_variables(void)
for (i = 0; ConfigureNamesEnum[i].gen.name; i++) for (i = 0; ConfigureNamesEnum[i].gen.name; i++)
guc_vars[num_vars++] = &ConfigureNamesEnum[i].gen; guc_vars[num_vars++] = &ConfigureNamesEnum[i].gen;
free(guc_variables); guc_free(guc_variables);
guc_variables = guc_vars; guc_variables = guc_vars;
num_guc_variables = num_vars; num_guc_variables = num_vars;
size_guc_variables = size_vars; size_guc_variables = size_vars;
@ -1001,7 +1041,7 @@ add_placeholder_variable(const char *name, int elevel)
gen->name = guc_strdup(elevel, name); gen->name = guc_strdup(elevel, name);
if (gen->name == NULL) if (gen->name == NULL)
{ {
free(var); guc_free(var);
return NULL; return NULL;
} }
@ -1020,8 +1060,8 @@ add_placeholder_variable(const char *name, int elevel)
if (!add_guc_variable((struct config_generic *) var, elevel)) if (!add_guc_variable((struct config_generic *) var, elevel))
{ {
free(unconstify(char *, gen->name)); guc_free(unconstify(char *, gen->name));
free(var); guc_free(var);
return NULL; return NULL;
} }
@ -1255,7 +1295,7 @@ InitializeGUCOptions(void)
pg_timezone_initialize(); pg_timezone_initialize();
/* /*
* Build sorted array of all GUC variables. * Create GUCMemoryContext and build sorted array of all GUC variables.
*/ */
build_guc_variables(); build_guc_variables();
@ -1482,6 +1522,7 @@ SelectConfigFiles(const char *userDoption, const char *progname)
{ {
char *configdir; char *configdir;
char *fname; char *fname;
bool fname_is_malloced;
struct stat stat_buf; struct stat stat_buf;
struct config_string *data_directory_rec; struct config_string *data_directory_rec;
@ -1509,12 +1550,16 @@ SelectConfigFiles(const char *userDoption, const char *progname)
* the same way by future backends. * the same way by future backends.
*/ */
if (ConfigFileName) if (ConfigFileName)
{
fname = make_absolute_path(ConfigFileName); fname = make_absolute_path(ConfigFileName);
fname_is_malloced = true;
}
else if (configdir) else if (configdir)
{ {
fname = guc_malloc(FATAL, fname = guc_malloc(FATAL,
strlen(configdir) + strlen(CONFIG_FILENAME) + 2); strlen(configdir) + strlen(CONFIG_FILENAME) + 2);
sprintf(fname, "%s/%s", configdir, CONFIG_FILENAME); sprintf(fname, "%s/%s", configdir, CONFIG_FILENAME);
fname_is_malloced = false;
} }
else else
{ {
@ -1530,7 +1575,11 @@ SelectConfigFiles(const char *userDoption, const char *progname)
* it can't be overridden later. * it can't be overridden later.
*/ */
SetConfigOption("config_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE); SetConfigOption("config_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
if (fname_is_malloced)
free(fname); free(fname);
else
guc_free(fname);
/* /*
* Now read the config file for the first time. * Now read the config file for the first time.
@ -1604,12 +1653,16 @@ SelectConfigFiles(const char *userDoption, const char *progname)
* Figure out where pg_hba.conf is, and make sure the path is absolute. * Figure out where pg_hba.conf is, and make sure the path is absolute.
*/ */
if (HbaFileName) if (HbaFileName)
{
fname = make_absolute_path(HbaFileName); fname = make_absolute_path(HbaFileName);
fname_is_malloced = true;
}
else if (configdir) else if (configdir)
{ {
fname = guc_malloc(FATAL, fname = guc_malloc(FATAL,
strlen(configdir) + strlen(HBA_FILENAME) + 2); strlen(configdir) + strlen(HBA_FILENAME) + 2);
sprintf(fname, "%s/%s", configdir, HBA_FILENAME); sprintf(fname, "%s/%s", configdir, HBA_FILENAME);
fname_is_malloced = false;
} }
else else
{ {
@ -1621,18 +1674,26 @@ SelectConfigFiles(const char *userDoption, const char *progname)
return false; return false;
} }
SetConfigOption("hba_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE); SetConfigOption("hba_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
if (fname_is_malloced)
free(fname); free(fname);
else
guc_free(fname);
/* /*
* Likewise for pg_ident.conf. * Likewise for pg_ident.conf.
*/ */
if (IdentFileName) if (IdentFileName)
{
fname = make_absolute_path(IdentFileName); fname = make_absolute_path(IdentFileName);
fname_is_malloced = true;
}
else if (configdir) else if (configdir)
{ {
fname = guc_malloc(FATAL, fname = guc_malloc(FATAL,
strlen(configdir) + strlen(IDENT_FILENAME) + 2); strlen(configdir) + strlen(IDENT_FILENAME) + 2);
sprintf(fname, "%s/%s", configdir, IDENT_FILENAME); sprintf(fname, "%s/%s", configdir, IDENT_FILENAME);
fname_is_malloced = false;
} }
else else
{ {
@ -1644,7 +1705,11 @@ SelectConfigFiles(const char *userDoption, const char *progname)
return false; return false;
} }
SetConfigOption("ident_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE); SetConfigOption("ident_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
if (fname_is_malloced)
free(fname); free(fname);
else
guc_free(fname);
free(configdir); free(configdir);
@ -2289,12 +2354,12 @@ ReportGUCOption(struct config_generic *record)
pq_endmessage(&msgbuf); pq_endmessage(&msgbuf);
/* /*
* We need a long-lifespan copy. If strdup() fails due to OOM, we'll * We need a long-lifespan copy. If guc_strdup() fails due to OOM,
* set last_reported to NULL and thereby possibly make a duplicate * we'll set last_reported to NULL and thereby possibly make a
* report later. * duplicate report later.
*/ */
free(record->last_reported); guc_free(record->last_reported);
record->last_reported = strdup(val); record->last_reported = guc_strdup(LOG, val);
} }
pfree(val); pfree(val);
@ -2893,7 +2958,7 @@ parse_and_validate_value(struct config_generic *record,
if (!call_string_check_hook(conf, &newval->stringval, newextra, if (!call_string_check_hook(conf, &newval->stringval, newextra,
source, elevel)) source, elevel))
{ {
free(newval->stringval); guc_free(newval->stringval);
newval->stringval = NULL; newval->stringval = NULL;
return false; return false;
} }
@ -3328,7 +3393,7 @@ set_config_option_ext(const char *name, const char *value,
{ {
/* Release newextra, unless it's reset_extra */ /* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra)) if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra); guc_free(newextra);
if (*conf->variable != newval) if (*conf->variable != newval)
{ {
@ -3387,7 +3452,7 @@ set_config_option_ext(const char *name, const char *value,
/* Perhaps we didn't install newextra anywhere */ /* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra)) if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra); guc_free(newextra);
break; break;
#undef newval #undef newval
@ -3426,7 +3491,7 @@ set_config_option_ext(const char *name, const char *value,
{ {
/* Release newextra, unless it's reset_extra */ /* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra)) if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra); guc_free(newextra);
if (*conf->variable != newval) if (*conf->variable != newval)
{ {
@ -3485,7 +3550,7 @@ set_config_option_ext(const char *name, const char *value,
/* Perhaps we didn't install newextra anywhere */ /* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra)) if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra); guc_free(newextra);
break; break;
#undef newval #undef newval
@ -3524,7 +3589,7 @@ set_config_option_ext(const char *name, const char *value,
{ {
/* Release newextra, unless it's reset_extra */ /* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra)) if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra); guc_free(newextra);
if (*conf->variable != newval) if (*conf->variable != newval)
{ {
@ -3583,7 +3648,7 @@ set_config_option_ext(const char *name, const char *value,
/* Perhaps we didn't install newextra anywhere */ /* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra)) if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra); guc_free(newextra);
break; break;
#undef newval #undef newval
@ -3617,7 +3682,7 @@ set_config_option_ext(const char *name, const char *value,
if (!call_string_check_hook(conf, &newval, &newextra, if (!call_string_check_hook(conf, &newval, &newextra,
source, elevel)) source, elevel))
{ {
free(newval); guc_free(newval);
return 0; return 0;
} }
} }
@ -3645,10 +3710,10 @@ set_config_option_ext(const char *name, const char *value,
/* Release newval, unless it's reset_val */ /* Release newval, unless it's reset_val */
if (newval && !string_field_used(conf, newval)) if (newval && !string_field_used(conf, newval))
free(newval); guc_free(newval);
/* Release newextra, unless it's reset_extra */ /* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra)) if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra); guc_free(newextra);
if (newval_different) if (newval_different)
{ {
@ -3709,10 +3774,10 @@ set_config_option_ext(const char *name, const char *value,
/* Perhaps we didn't install newval anywhere */ /* Perhaps we didn't install newval anywhere */
if (newval && !string_field_used(conf, newval)) if (newval && !string_field_used(conf, newval))
free(newval); guc_free(newval);
/* Perhaps we didn't install newextra anywhere */ /* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra)) if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra); guc_free(newextra);
break; break;
#undef newval #undef newval
@ -3751,7 +3816,7 @@ set_config_option_ext(const char *name, const char *value,
{ {
/* Release newextra, unless it's reset_extra */ /* Release newextra, unless it's reset_extra */
if (newextra && !extra_field_used(&conf->gen, newextra)) if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra); guc_free(newextra);
if (*conf->variable != newval) if (*conf->variable != newval)
{ {
@ -3810,7 +3875,7 @@ set_config_option_ext(const char *name, const char *value,
/* Perhaps we didn't install newextra anywhere */ /* Perhaps we didn't install newextra anywhere */
if (newextra && !extra_field_used(&conf->gen, newextra)) if (newextra && !extra_field_used(&conf->gen, newextra))
free(newextra); guc_free(newextra);
break; break;
#undef newval #undef newval
@ -3848,7 +3913,7 @@ set_config_sourcefile(const char *name, char *sourcefile, int sourceline)
return; return;
sourcefile = guc_strdup(elevel, sourcefile); sourcefile = guc_strdup(elevel, sourcefile);
free(record->sourcefile); guc_free(record->sourcefile);
record->sourcefile = sourcefile; record->sourcefile = sourcefile;
record->sourceline = sourceline; record->sourceline = sourceline;
} }
@ -4239,8 +4304,8 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
name, value))); name, value)));
if (record->vartype == PGC_STRING && newval.stringval != NULL) if (record->vartype == PGC_STRING && newval.stringval != NULL)
free(newval.stringval); guc_free(newval.stringval);
free(newextra); guc_free(newextra);
/* /*
* We must also reject values containing newlines, because the * We must also reject values containing newlines, because the
@ -4535,7 +4600,7 @@ define_custom_variable(struct config_generic *variable)
set_string_field(pHolder, pHolder->variable, NULL); set_string_field(pHolder, pHolder->variable, NULL);
set_string_field(pHolder, &pHolder->reset_val, NULL); set_string_field(pHolder, &pHolder->reset_val, NULL);
free(pHolder); guc_free(pHolder);
} }
/* /*
@ -4814,7 +4879,7 @@ MarkGUCPrefixReserved(const char *className)
} }
/* And remember the name so we can prevent future mistakes. */ /* 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)); reserved_class_prefix = lappend(reserved_class_prefix, pstrdup(className));
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
} }
@ -5287,9 +5352,9 @@ read_nondefault_variables(void)
if (varsourcefile[0]) if (varsourcefile[0])
set_config_sourcefile(varname, varsourcefile, varsourceline); set_config_sourcefile(varname, varsourcefile, varsourceline);
free(varname); guc_free(varname);
free(varvalue); guc_free(varvalue);
free(varsourcefile); guc_free(varsourcefile);
} }
FreeFile(fp); FreeFile(fp);
@ -5731,9 +5796,9 @@ RestoreGUCState(void *gucstate)
* pointers. * pointers.
*/ */
Assert(gconf->stack == NULL); Assert(gconf->stack == NULL);
free(gconf->extra); guc_free(gconf->extra);
free(gconf->last_reported); guc_free(gconf->last_reported);
free(gconf->sourcefile); guc_free(gconf->sourcefile);
switch (gconf->vartype) switch (gconf->vartype)
{ {
case PGC_BOOL: case PGC_BOOL:
@ -5741,7 +5806,7 @@ RestoreGUCState(void *gucstate)
struct config_bool *conf = (struct config_bool *) gconf; struct config_bool *conf = (struct config_bool *) gconf;
if (conf->reset_extra && conf->reset_extra != gconf->extra) if (conf->reset_extra && conf->reset_extra != gconf->extra)
free(conf->reset_extra); guc_free(conf->reset_extra);
break; break;
} }
case PGC_INT: case PGC_INT:
@ -5749,7 +5814,7 @@ RestoreGUCState(void *gucstate)
struct config_int *conf = (struct config_int *) gconf; struct config_int *conf = (struct config_int *) gconf;
if (conf->reset_extra && conf->reset_extra != gconf->extra) if (conf->reset_extra && conf->reset_extra != gconf->extra)
free(conf->reset_extra); guc_free(conf->reset_extra);
break; break;
} }
case PGC_REAL: case PGC_REAL:
@ -5757,18 +5822,18 @@ RestoreGUCState(void *gucstate)
struct config_real *conf = (struct config_real *) gconf; struct config_real *conf = (struct config_real *) gconf;
if (conf->reset_extra && conf->reset_extra != gconf->extra) if (conf->reset_extra && conf->reset_extra != gconf->extra)
free(conf->reset_extra); guc_free(conf->reset_extra);
break; break;
} }
case PGC_STRING: case PGC_STRING:
{ {
struct config_string *conf = (struct config_string *) gconf; struct config_string *conf = (struct config_string *) gconf;
free(*conf->variable); guc_free(*conf->variable);
if (conf->reset_val && conf->reset_val != *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) if (conf->reset_extra && conf->reset_extra != gconf->extra)
free(conf->reset_extra); guc_free(conf->reset_extra);
break; break;
} }
case PGC_ENUM: case PGC_ENUM:
@ -5776,7 +5841,7 @@ RestoreGUCState(void *gucstate)
struct config_enum *conf = (struct config_enum *) gconf; struct config_enum *conf = (struct config_enum *) gconf;
if (conf->reset_extra && conf->reset_extra != gconf->extra) if (conf->reset_extra && conf->reset_extra != gconf->extra)
free(conf->reset_extra); guc_free(conf->reset_extra);
break; break;
} }
} }
@ -5838,7 +5903,7 @@ RestoreGUCState(void *gucstate)
/* /*
* A little "long argument" simulation, although not quite GNU * A little "long argument" simulation, although not quite GNU
* compliant. Takes a string of the form "some-option=some value" and * 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 * storage. Note that '-' is converted to '_' in the option name. If
* there is no '=' in the input string then value will be NULL. * there is no '=' in the input string then value will be NULL.
*/ */
@ -5856,15 +5921,15 @@ ParseLongOption(const char *string, char **name, char **value)
if (string[equal_pos] == '=') if (string[equal_pos] == '=')
{ {
*name = guc_malloc(FATAL, equal_pos + 1); *name = palloc(equal_pos + 1);
strlcpy(*name, string, equal_pos + 1); strlcpy(*name, string, equal_pos + 1);
*value = guc_strdup(FATAL, &string[equal_pos + 1]); *value = pstrdup(&string[equal_pos + 1]);
} }
else else
{ {
/* no equal sign in string */ /* no equal sign in string */
*name = guc_strdup(FATAL, string); *name = pstrdup(string);
*value = NULL; *value = NULL;
} }
@ -5898,8 +5963,6 @@ ProcessGUCArray(ArrayType *array,
char *s; char *s;
char *name; char *name;
char *value; char *value;
char *namecopy;
char *valuecopy;
d = array_ref(array, 1, &i, d = array_ref(array, 1, &i,
-1 /* varlenarray */ , -1 /* varlenarray */ ,
@ -5920,22 +5983,16 @@ ProcessGUCArray(ArrayType *array,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("could not parse setting for parameter \"%s\"", errmsg("could not parse setting for parameter \"%s\"",
name))); name)));
free(name); pfree(name);
continue; continue;
} }
/* free malloc'd strings immediately to avoid leak upon error */ (void) set_config_option(name, value,
namecopy = pstrdup(name);
free(name);
valuecopy = pstrdup(value);
free(value);
(void) set_config_option(namecopy, valuecopy,
context, source, context, source,
action, true, 0, false); action, true, 0, false);
pfree(namecopy); pfree(name);
pfree(valuecopy); pfree(value);
pfree(s); pfree(s);
} }
} }
@ -6399,7 +6456,7 @@ call_string_check_hook(struct config_string *conf, char **newval, void **extra,
} }
PG_CATCH(); PG_CATCH();
{ {
free(*newval); guc_free(*newval);
PG_RE_THROW(); PG_RE_THROW();
} }
PG_END_TRY(); PG_END_TRY();

View File

@ -439,7 +439,7 @@ ParseTzFile(const char *filename, int depth,
* load_tzoffsets --- read and parse the specified timezone offset file * load_tzoffsets --- read and parse the specified timezone offset file
* *
* On success, return a filled-in TimeZoneAbbrevTable, which must have been * 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. * and friends to give details of the problem.
*/ */
TimeZoneAbbrevTable * TimeZoneAbbrevTable *

View File

@ -401,6 +401,7 @@ extern ArrayType *GUCArrayReset(ArrayType *array);
extern void *guc_malloc(int elevel, size_t size); extern void *guc_malloc(int elevel, size_t size);
extern pg_nodiscard void *guc_realloc(int elevel, void *old, 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 char *guc_strdup(int elevel, const char *src);
extern void guc_free(void *ptr);
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
extern void write_nondefault_variables(GucContext context); extern void write_nondefault_variables(GucContext context);

View File

@ -114,7 +114,7 @@ plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source)
list_free(elemlist); list_free(elemlist);
} }
myextra = (int *) malloc(sizeof(int)); myextra = (int *) guc_malloc(LOG, sizeof(int));
if (!myextra) if (!myextra)
return false; return false;
*myextra = extrachecks; *myextra = extrachecks;