mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Avoid leaking memory in RestoreGUCState(), and improve comments.
RestoreGUCState applied InitializeOneGUCOption to already-live
GUC entries, causing any malloc'd subsidiary data to be forgotten.
We do want the effect of resetting the GUC to its compiled-in
default, and InitializeOneGUCOption seems like the best way to do
that, so add code to free any existing subsidiary data beforehand.
The interaction between can_skip_gucvar, SerializeGUCState, and
RestoreGUCState is way more subtle than their opaque comments
would suggest to an unwary reader. Rewrite and enlarge the
comments to try to make it clearer what's happening.
Remove a long-obsolete assertion in read_nondefault_variables: the
behavior of set_config_option hasn't depended on IsInitProcessingMode
since f5d9698a8
installed a better way of controlling it.
Although this is fixing a clear memory leak, the leak is quite unlikely
to involve any large amount of data, and it can only happen once in the
lifetime of a worker process. So it seems unnecessary to take any
risk of back-patching.
Discussion: https://postgr.es/m/4105247.1616174862@sss.pgh.pa.us
This commit is contained in:
@ -7121,6 +7121,10 @@ parse_and_validate_value(struct config_generic *record,
|
|||||||
* its standard choice of ereport level. However some callers need to be
|
* its standard choice of ereport level. However some callers need to be
|
||||||
* able to override that choice; they should pass the ereport level to use.
|
* able to override that choice; they should pass the ereport level to use.
|
||||||
*
|
*
|
||||||
|
* is_reload should be true only when called from read_nondefault_variables()
|
||||||
|
* or RestoreGUCState(), where we are trying to load some other process's
|
||||||
|
* GUC settings into a new process.
|
||||||
|
*
|
||||||
* Return value:
|
* Return value:
|
||||||
* +1: the value is valid and was successfully applied.
|
* +1: the value is valid and was successfully applied.
|
||||||
* 0: the name or value is invalid (but see below).
|
* 0: the name or value is invalid (but see below).
|
||||||
@ -10258,12 +10262,6 @@ read_nondefault_variables(void)
|
|||||||
GucSource varsource;
|
GucSource varsource;
|
||||||
GucContext varscontext;
|
GucContext varscontext;
|
||||||
|
|
||||||
/*
|
|
||||||
* Assert that PGC_BACKEND/PGC_SU_BACKEND case in set_config_option() will
|
|
||||||
* do the right thing.
|
|
||||||
*/
|
|
||||||
Assert(IsInitProcessingMode());
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open file
|
* Open file
|
||||||
*/
|
*/
|
||||||
@ -10317,30 +10315,43 @@ read_nondefault_variables(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* can_skip_gucvar:
|
* can_skip_gucvar:
|
||||||
* When serializing, determine whether to skip this GUC. When restoring, the
|
* Decide whether SerializeGUCState can skip sending this GUC variable,
|
||||||
* negation of this test determines whether to restore the compiled-in default
|
* or whether RestoreGUCState can skip resetting this GUC to default.
|
||||||
* value before processing serialized values.
|
|
||||||
*
|
*
|
||||||
* A PGC_S_DEFAULT setting on the serialize side will typically match new
|
* It is somewhat magical and fragile that the same test works for both cases.
|
||||||
* postmaster children, but that can be false when got_SIGHUP == true and the
|
* Realize in particular that we are very likely selecting different sets of
|
||||||
* pending configuration change modifies this setting. Nonetheless, we omit
|
* GUCs on the leader and worker sides! Be sure you've understood the
|
||||||
* PGC_S_DEFAULT settings from serialization and make up for that by restoring
|
* comments here and in RestoreGUCState thoroughly before changing this.
|
||||||
* defaults before applying serialized values.
|
|
||||||
*
|
|
||||||
* PGC_POSTMASTER variables always have the same value in every child of a
|
|
||||||
* particular postmaster. Most PGC_INTERNAL variables are compile-time
|
|
||||||
* constants; a few, like server_encoding and lc_ctype, are handled specially
|
|
||||||
* outside the serialize/restore procedure. Therefore, SerializeGUCState()
|
|
||||||
* never sends these, and RestoreGUCState() never changes them.
|
|
||||||
*
|
|
||||||
* Role is a special variable in the sense that its current value can be an
|
|
||||||
* invalid value and there are multiple ways by which that can happen (like
|
|
||||||
* after setting the role, someone drops it). So we handle it outside of
|
|
||||||
* serialize/restore machinery.
|
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
can_skip_gucvar(struct config_generic *gconf)
|
can_skip_gucvar(struct config_generic *gconf)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* We can skip GUCs that are guaranteed to have the same values in leaders
|
||||||
|
* and workers. (Note it is critical that the leader and worker have the
|
||||||
|
* same idea of which GUCs fall into this category. It's okay to consider
|
||||||
|
* context and name for this purpose, since those are unchanging
|
||||||
|
* properties of a GUC.)
|
||||||
|
*
|
||||||
|
* PGC_POSTMASTER variables always have the same value in every child of a
|
||||||
|
* particular postmaster, so the worker will certainly have the right
|
||||||
|
* value already. Likewise, PGC_INTERNAL variables are set by special
|
||||||
|
* mechanisms (if indeed they aren't compile-time constants). So we may
|
||||||
|
* always skip these.
|
||||||
|
*
|
||||||
|
* Role must be handled specially because its current value can be an
|
||||||
|
* invalid value (for instance, if someone dropped the role since we set
|
||||||
|
* it). So if we tried to serialize it normally, we might get a failure.
|
||||||
|
* We skip it here, and use another mechanism to ensure the worker has the
|
||||||
|
* right value.
|
||||||
|
*
|
||||||
|
* For all other GUCs, we skip if the GUC has its compiled-in default
|
||||||
|
* value (i.e., source == PGC_S_DEFAULT). On the leader side, this means
|
||||||
|
* we don't send GUCs that have their default values, which typically
|
||||||
|
* saves lots of work. On the worker side, this means we don't need to
|
||||||
|
* reset the GUC to default because it already has that value. See
|
||||||
|
* comments in RestoreGUCState for more info.
|
||||||
|
*/
|
||||||
return gconf->context == PGC_POSTMASTER ||
|
return gconf->context == PGC_POSTMASTER ||
|
||||||
gconf->context == PGC_INTERNAL || gconf->source == PGC_S_DEFAULT ||
|
gconf->context == PGC_INTERNAL || gconf->source == PGC_S_DEFAULT ||
|
||||||
strcmp(gconf->name, "role") == 0;
|
strcmp(gconf->name, "role") == 0;
|
||||||
@ -10358,6 +10369,7 @@ estimate_variable_size(struct config_generic *gconf)
|
|||||||
Size size;
|
Size size;
|
||||||
Size valsize = 0;
|
Size valsize = 0;
|
||||||
|
|
||||||
|
/* Skippable GUCs consume zero space. */
|
||||||
if (can_skip_gucvar(gconf))
|
if (can_skip_gucvar(gconf))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -10522,6 +10534,7 @@ static void
|
|||||||
serialize_variable(char **destptr, Size *maxbytes,
|
serialize_variable(char **destptr, Size *maxbytes,
|
||||||
struct config_generic *gconf)
|
struct config_generic *gconf)
|
||||||
{
|
{
|
||||||
|
/* Ignore skippable GUCs. */
|
||||||
if (can_skip_gucvar(gconf))
|
if (can_skip_gucvar(gconf))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -10669,8 +10682,14 @@ guc_restore_error_context_callback(void *arg)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* RestoreGUCState:
|
* RestoreGUCState:
|
||||||
* Reads the GUC state at the specified address and updates the GUCs with the
|
* Reads the GUC state at the specified address and sets this process's
|
||||||
* values read from the GUC state.
|
* GUCs to match.
|
||||||
|
*
|
||||||
|
* Note that this provides the worker with only a very shallow view of the
|
||||||
|
* leader's GUC state: we'll know about the currently active values, but not
|
||||||
|
* about stacked or reset values. That's fine since the worker is just
|
||||||
|
* executing one part of a query, within which the active values won't change
|
||||||
|
* and the stacked values are invisible.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
RestoreGUCState(void *gucstate)
|
RestoreGUCState(void *gucstate)
|
||||||
@ -10687,10 +10706,100 @@ RestoreGUCState(void *gucstate)
|
|||||||
int i;
|
int i;
|
||||||
ErrorContextCallback error_context_callback;
|
ErrorContextCallback error_context_callback;
|
||||||
|
|
||||||
/* See comment at can_skip_gucvar(). */
|
/*
|
||||||
|
* First, ensure that all potentially-shippable GUCs are reset to their
|
||||||
|
* default values. We must not touch those GUCs that the leader will
|
||||||
|
* never ship, while there is no need to touch those that are shippable
|
||||||
|
* but already have their default values. Thus, this ends up being the
|
||||||
|
* same test that SerializeGUCState uses, even though the sets of
|
||||||
|
* variables involved may well be different since the leader's set of
|
||||||
|
* variables-not-at-default-values can differ from the set that are
|
||||||
|
* not-default in this freshly started worker.
|
||||||
|
*
|
||||||
|
* Once we have set all the potentially-shippable GUCs to default values,
|
||||||
|
* restoring the GUCs that the leader sent (because they had non-default
|
||||||
|
* values over there) leads us to exactly the set of GUC values that the
|
||||||
|
* leader has. This is true even though the worker may have initially
|
||||||
|
* absorbed postgresql.conf settings that the leader hasn't yet seen, or
|
||||||
|
* ALTER USER/DATABASE SET settings that were established after the leader
|
||||||
|
* started.
|
||||||
|
*
|
||||||
|
* Note that ensuring all the potential target GUCs are at PGC_S_DEFAULT
|
||||||
|
* also ensures that set_config_option won't refuse to set them because of
|
||||||
|
* source-priority comparisons.
|
||||||
|
*/
|
||||||
for (i = 0; i < num_guc_variables; i++)
|
for (i = 0; i < num_guc_variables; i++)
|
||||||
if (!can_skip_gucvar(guc_variables[i]))
|
{
|
||||||
InitializeOneGUCOption(guc_variables[i]);
|
struct config_generic *gconf = guc_variables[i];
|
||||||
|
|
||||||
|
/* Do nothing if non-shippable or if already at PGC_S_DEFAULT. */
|
||||||
|
if (can_skip_gucvar(gconf))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can use InitializeOneGUCOption to reset the GUC to default, but
|
||||||
|
* first we must free any existing subsidiary data to avoid leaking
|
||||||
|
* memory. The stack must be empty, but we have to clean up all other
|
||||||
|
* fields. Beware that there might be duplicate value or "extra"
|
||||||
|
* pointers.
|
||||||
|
*/
|
||||||
|
Assert(gconf->stack == NULL);
|
||||||
|
if (gconf->extra)
|
||||||
|
free(gconf->extra);
|
||||||
|
if (gconf->last_reported) /* probably can't happen */
|
||||||
|
free(gconf->last_reported);
|
||||||
|
if (gconf->sourcefile)
|
||||||
|
free(gconf->sourcefile);
|
||||||
|
switch (gconf->vartype)
|
||||||
|
{
|
||||||
|
case PGC_BOOL:
|
||||||
|
{
|
||||||
|
struct config_bool *conf = (struct config_bool *) gconf;
|
||||||
|
|
||||||
|
if (conf->reset_extra && conf->reset_extra != gconf->extra)
|
||||||
|
free(conf->reset_extra);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PGC_INT:
|
||||||
|
{
|
||||||
|
struct config_int *conf = (struct config_int *) gconf;
|
||||||
|
|
||||||
|
if (conf->reset_extra && conf->reset_extra != gconf->extra)
|
||||||
|
free(conf->reset_extra);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PGC_REAL:
|
||||||
|
{
|
||||||
|
struct config_real *conf = (struct config_real *) gconf;
|
||||||
|
|
||||||
|
if (conf->reset_extra && conf->reset_extra != gconf->extra)
|
||||||
|
free(conf->reset_extra);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PGC_STRING:
|
||||||
|
{
|
||||||
|
struct config_string *conf = (struct config_string *) gconf;
|
||||||
|
|
||||||
|
if (*conf->variable)
|
||||||
|
free(*conf->variable);
|
||||||
|
if (conf->reset_val && conf->reset_val != *conf->variable)
|
||||||
|
free(conf->reset_val);
|
||||||
|
if (conf->reset_extra && conf->reset_extra != gconf->extra)
|
||||||
|
free(conf->reset_extra);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PGC_ENUM:
|
||||||
|
{
|
||||||
|
struct config_enum *conf = (struct config_enum *) gconf;
|
||||||
|
|
||||||
|
if (conf->reset_extra && conf->reset_extra != gconf->extra)
|
||||||
|
free(conf->reset_extra);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Now we can reset the struct to PGS_S_DEFAULT state. */
|
||||||
|
InitializeOneGUCOption(gconf);
|
||||||
|
}
|
||||||
|
|
||||||
/* First item is the length of the subsequent data */
|
/* First item is the length of the subsequent data */
|
||||||
memcpy(&len, gucstate, sizeof(len));
|
memcpy(&len, gucstate, sizeof(len));
|
||||||
@ -10704,6 +10813,7 @@ RestoreGUCState(void *gucstate)
|
|||||||
error_context_callback.arg = NULL;
|
error_context_callback.arg = NULL;
|
||||||
error_context_stack = &error_context_callback;
|
error_context_stack = &error_context_callback;
|
||||||
|
|
||||||
|
/* Restore all the listed GUCs. */
|
||||||
while (srcptr < srcend)
|
while (srcptr < srcend)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
|
Reference in New Issue
Block a user