postgres.git / summary / log / commit / refs

commit    43ca5e07d1bb160deeddaba8456d462a460dc4f8
Author:   Tom Lane <tgl@sss.pgh.pa.us>
Date:     Mon Oct 12 17:31:24 2020 +0000
First released in: 14.0, 9.5.24, 9.6.20, 10.15, 11.10, 12.5, 13.1

    Fix memory leak when guc.c decides a setting can't be applied now.
    
    The prohibitValueChange code paths in set_config_option(), which
    are executed whenever we re-read a PGC_POSTMASTER variable from
    postgresql.conf, neglected to free anything before exiting.  Thus
    we'd leak the proposed new value of a PGC_STRING variable, as noted
    by BoChen in bug #16666.  For all variable types, if the check hook
    creates an "extra" chunk, we'd also leak that.
    
    These are malloc not palloc chunks, so there is no mechanism for
    recovering the leaks before process exit.  Fortunately, the values
    are typically not very large, meaning you'd have to go through an
    awful lot of SIGHUP configuration-reload cycles to make the leakage
    amount to anything.  Still, for a long-lived postmaster process it
    could potentially be a problem.
    
    Oversight in commit 2594cf0e8.  Back-patch to all supported branches.
    
    Discussion: https://postgr.es/m/16666-2c41a4eec61b03e1@postgresql.org


src/backend/utils/misc/guc.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 569439fec75..76e0e171c89 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -5917,6 +5917,10 @@ set_config_option(const char *name, const char *value, if (prohibitValueChange) { + /* Release newextra, unless it's reset_extra */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); + if (*conf->variable != newval) { record->status |= GUC_PENDING_RESTART; @@ -6007,6 +6011,10 @@ set_config_option(const char *name, const char *value, if (prohibitValueChange) { + /* Release newextra, unless it's reset_extra */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); + if (*conf->variable != newval) { record->status |= GUC_PENDING_RESTART; @@ -6097,6 +6105,10 @@ set_config_option(const char *name, const char *value, if (prohibitValueChange) { + /* Release newextra, unless it's reset_extra */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); + if (*conf->variable != newval) { record->status |= GUC_PENDING_RESTART; @@ -6203,9 +6215,21 @@ set_config_option(const char *name, const char *value, if (prohibitValueChange) { + bool newval_different; + /* newval shouldn't be NULL, so we're a bit sloppy here */ - if (*conf->variable == NULL || newval == NULL || - strcmp(*conf->variable, newval) != 0) + newval_different = (*conf->variable == NULL || + newval == NULL || + strcmp(*conf->variable, newval) != 0); + + /* Release newval, unless it's reset_val */ + if (newval && !string_field_used(conf, newval)) + free(newval); + /* Release newextra, unless it's reset_extra */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); + + if (newval_different) { record->status |= GUC_PENDING_RESTART; ereport(elevel, @@ -6300,6 +6324,10 @@ set_config_option(const char *name, const char *value, if (prohibitValueChange) { + /* Release newextra, unless it's reset_extra */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); + if (*conf->variable != newval) { record->status |= GUC_PENDING_RESTART; [parent: abfce4e46870]