Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Avoid multiple free_struct_lconv() calls on same data.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 29 Feb 2016 04:39:20 +0000 (23:39 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 29 Feb 2016 04:40:04 +0000 (23:40 -0500)
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.

src/backend/utils/adt/pg_locale.c

index d91959ea7f8401366ee43510126e471b987e9d62..76acdcb274f5f18cad142eadeb31caa007bbd919 100644 (file)
@@ -387,9 +387,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)
@@ -441,6 +438,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;
@@ -457,7 +455,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);
@@ -521,7 +524,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;
@@ -534,6 +539,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)