aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2016-02-28 23:39:20 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2016-02-28 23:40:35 -0500
commit7d6c58aa1114a6482722c125d44b4eb15fe5df18 (patch)
treed71e811b7a4c1656a980698d6b0185e97a2b42c9
parent5d312ef1b2b38d0db90394691694238194172e84 (diff)
downloadpostgresql-7d6c58aa1114a6482722c125d44b4eb15fe5df18.tar.gz
postgresql-7d6c58aa1114a6482722c125d44b4eb15fe5df18.zip
Avoid multiple free_struct_lconv() calls on same data.
A failure partway through PGLC_localeconv() led to a situation where the next call would call free_struct_lconv() a second time, leading to free() on already-freed strings, typically leading to a core dump. Add a flag to remember whether we need to do that. Per report from Thom Brown. His example case only provokes the failure as far back as 9.4, but nonetheless this code is obviously broken, so back-patch to all supported branches.
-rw-r--r--src/backend/utils/adt/pg_locale.c16
1 files changed, 11 insertions, 5 deletions
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 45069b7a664..2e8b625db93 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -348,9 +348,6 @@ assign_locale_messages(const char *newval, void *extra)
static void
free_struct_lconv(struct lconv * s)
{
- if (s == NULL)
- return;
-
if (s->currency_symbol)
free(s->currency_symbol);
if (s->decimal_point)
@@ -404,6 +401,7 @@ struct lconv *
PGLC_localeconv(void)
{
static struct lconv CurrentLocaleConv;
+ static bool CurrentLocaleConvAllocated = false;
struct lconv *extlconv;
char *save_lc_monetary;
char *save_lc_numeric;
@@ -420,7 +418,12 @@ PGLC_localeconv(void)
if (CurrentLocaleConvValid)
return &CurrentLocaleConv;
- free_struct_lconv(&CurrentLocaleConv);
+ /* Free any already-allocated storage */
+ if (CurrentLocaleConvAllocated)
+ {
+ free_struct_lconv(&CurrentLocaleConv);
+ CurrentLocaleConvAllocated = false;
+ }
/* Save user's values of monetary and numeric locales */
save_lc_monetary = setlocale(LC_MONETARY, NULL);
@@ -484,7 +487,9 @@ PGLC_localeconv(void)
/*
* Must copy all values since restoring internal settings may overwrite
- * localeconv()'s results.
+ * localeconv()'s results. Note that if we were to fail within this
+ * sequence before reaching "CurrentLocaleConvAllocated = true", we could
+ * leak some memory --- but not much, so it's not worth agonizing over.
*/
CurrentLocaleConv = *extlconv;
CurrentLocaleConv.decimal_point = decimal_point;
@@ -497,6 +502,7 @@ PGLC_localeconv(void)
CurrentLocaleConv.mon_thousands_sep = db_encoding_strdup(encoding, extlconv->mon_thousands_sep);
CurrentLocaleConv.negative_sign = db_encoding_strdup(encoding, extlconv->negative_sign);
CurrentLocaleConv.positive_sign = db_encoding_strdup(encoding, extlconv->positive_sign);
+ CurrentLocaleConvAllocated = true;
/* Try to restore internal settings */
if (save_lc_monetary)