Skip to content

Commit fc552f8

Browse files
committed
Don't leak malloc'd strings when a GUC setting is rejected.
Because guc.c prefers to keep all its string values in malloc'd not palloc'd storage, it has to be more careful than usual to avoid leaks. Error exits out of string GUC hook checks failed to clear the proposed value string, and error exits out of ProcessGUCArray() failed to clear the malloc'd results of ParseLongOption(). Found via valgrind testing. This problem is ancient, so back-patch to all supported branches. Discussion: https://postgr.es/m/3816764.1616104288@sss.pgh.pa.us
1 parent 92bc14a commit fc552f8

File tree

1 file changed

+47
-24
lines changed
  • src/backend/utils/misc

1 file changed

+47
-24
lines changed

src/backend/utils/misc/guc.c

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10361,6 +10361,8 @@ ProcessGUCArray(ArrayType *array,
1036110361
char *s;
1036210362
char *name;
1036310363
char *value;
10364+
char *namecopy;
10365+
char *valuecopy;
1036410366

1036510367
d = array_ref(array, 1, &i,
1036610368
-1 /* varlenarray */ ,
@@ -10385,13 +10387,18 @@ ProcessGUCArray(ArrayType *array,
1038510387
continue;
1038610388
}
1038710389

10388-
(void) set_config_option(name, value,
10390+
/* free malloc'd strings immediately to avoid leak upon error */
10391+
namecopy = pstrdup(name);
10392+
free(name);
10393+
valuecopy = pstrdup(value);
10394+
free(value);
10395+
10396+
(void) set_config_option(namecopy, valuecopy,
1038910397
context, source,
1039010398
action, true, 0, false);
1039110399

10392-
free(name);
10393-
if (value)
10394-
free(value);
10400+
pfree(namecopy);
10401+
pfree(valuecopy);
1039510402
pfree(s);
1039610403
}
1039710404
}
@@ -10823,34 +10830,50 @@ static bool
1082310830
call_string_check_hook(struct config_string *conf, char **newval, void **extra,
1082410831
GucSource source, int elevel)
1082510832
{
10833+
volatile bool result = true;
10834+
1082610835
/* Quick success if no hook */
1082710836
if (!conf->check_hook)
1082810837
return true;
1082910838

10830-
/* Reset variables that might be set by hook */
10831-
GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
10832-
GUC_check_errmsg_string = NULL;
10833-
GUC_check_errdetail_string = NULL;
10834-
GUC_check_errhint_string = NULL;
10839+
/*
10840+
* If elevel is ERROR, or if the check_hook itself throws an elog
10841+
* (undesirable, but not always avoidable), make sure we don't leak the
10842+
* already-malloc'd newval string.
10843+
*/
10844+
PG_TRY();
10845+
{
10846+
/* Reset variables that might be set by hook */
10847+
GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
10848+
GUC_check_errmsg_string = NULL;
10849+
GUC_check_errdetail_string = NULL;
10850+
GUC_check_errhint_string = NULL;
1083510851

10836-
if (!conf->check_hook(newval, extra, source))
10852+
if (!conf->check_hook(newval, extra, source))
10853+
{
10854+
ereport(elevel,
10855+
(errcode(GUC_check_errcode_value),
10856+
GUC_check_errmsg_string ?
10857+
errmsg_internal("%s", GUC_check_errmsg_string) :
10858+
errmsg("invalid value for parameter \"%s\": \"%s\"",
10859+
conf->gen.name, *newval ? *newval : ""),
10860+
GUC_check_errdetail_string ?
10861+
errdetail_internal("%s", GUC_check_errdetail_string) : 0,
10862+
GUC_check_errhint_string ?
10863+
errhint("%s", GUC_check_errhint_string) : 0));
10864+
/* Flush any strings created in ErrorContext */
10865+
FlushErrorState();
10866+
result = false;
10867+
}
10868+
}
10869+
PG_CATCH();
1083710870
{
10838-
ereport(elevel,
10839-
(errcode(GUC_check_errcode_value),
10840-
GUC_check_errmsg_string ?
10841-
errmsg_internal("%s", GUC_check_errmsg_string) :
10842-
errmsg("invalid value for parameter \"%s\": \"%s\"",
10843-
conf->gen.name, *newval ? *newval : ""),
10844-
GUC_check_errdetail_string ?
10845-
errdetail_internal("%s", GUC_check_errdetail_string) : 0,
10846-
GUC_check_errhint_string ?
10847-
errhint("%s", GUC_check_errhint_string) : 0));
10848-
/* Flush any strings created in ErrorContext */
10849-
FlushErrorState();
10850-
return false;
10871+
free(*newval);
10872+
PG_RE_THROW();
1085110873
}
10874+
PG_END_TRY();
1085210875

10853-
return true;
10876+
return result;
1085410877
}
1085510878

1085610879
static bool

0 commit comments

Comments
 (0)