From 407b50f2d421bca5b134a0033176ea8f8c68dc6b Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 14 Oct 2022 12:10:48 -0400 Subject: [PATCH] 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 --- src/backend/bootstrap/bootstrap.c | 4 +- src/backend/commands/tablespace.c | 4 +- src/backend/commands/variable.c | 34 ++--- src/backend/postmaster/postmaster.c | 4 +- src/backend/replication/syncrep.c | 4 +- src/backend/tcop/postgres.c | 4 +- src/backend/utils/adt/datetime.c | 7 +- src/backend/utils/cache/ts_cache.c | 6 +- src/backend/utils/misc/README | 15 +- src/backend/utils/misc/guc.c | 225 +++++++++++++++++----------- src/backend/utils/misc/tzparser.c | 2 +- src/include/utils/guc.h | 1 + src/pl/plpgsql/src/pl_handler.c | 2 +- 13 files changed, 185 insertions(+), 127 deletions(-) diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 22635f8094..58247d826d 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 e555fb3150..791bac6715 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 383bc4776e..30fb576ac3 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 27dee29f42..a9a1851c94 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -3871,8 +3871,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 4e9935e01d..74b6807098 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 f997ec0f82..3a4c4814a7 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,20 @@ 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) + { + /* This is to help catch old code that malloc's GUC data. */ + Assert(GetMemoryChunkContext(old) == GUCMemoryContext); + 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, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); @@ -634,15 +649,29 @@ 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) + { + /* 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 @@ -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 */ 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 */ 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. * * 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 +846,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 +858,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 +935,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 +1041,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 +1060,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 +1295,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 +1522,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 +1550,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 +1575,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 +1653,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 +1674,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 +1705,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 +2354,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 +2958,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; } @@ -3328,7 +3393,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) { @@ -3387,7 +3452,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 @@ -3426,7 +3491,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) { @@ -3485,7 +3550,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 @@ -3524,7 +3589,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) { @@ -3583,7 +3648,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 @@ -3617,7 +3682,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; } } @@ -3645,10 +3710,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) { @@ -3709,10 +3774,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 @@ -3751,7 +3816,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) { @@ -3810,7 +3875,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 @@ -3848,7 +3913,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; } @@ -4239,8 +4304,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 @@ -4535,7 +4600,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); } /* @@ -4814,7 +4879,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); } @@ -5287,9 +5352,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); @@ -5731,9 +5796,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: @@ -5741,7 +5806,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: @@ -5749,7 +5814,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: @@ -5757,18 +5822,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: @@ -5776,7 +5841,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; } } @@ -5838,7 +5903,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. */ @@ -5856,15 +5921,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; } @@ -5898,8 +5963,6 @@ ProcessGUCArray(ArrayType *array, char *s; char *name; char *value; - char *namecopy; - char *valuecopy; d = array_ref(array, 1, &i, -1 /* varlenarray */ , @@ -5920,22 +5983,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); } } @@ -6399,7 +6456,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 1788361974..c8b65c5bb8 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -401,6 +401,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;