[PATCH] locale: memory leak in newlocale (BZ #25770)

Dmitry Kovalenko d.kovalenko@postgrespro.ru
Sat May 17 11:40:13 GMT 2025


It is a fix for memory leak in the function 'newlocale' (the real name 
is __newlocale).

There is not released a memory for the local buffer 'locale_path'.

https://sourceware.org/bugzilla/show_bug.cgi?id=25770

I made the minimal changes to avoid the unnecessary 
reformating/refactoring of source code.

Potentail problems:
- in 'label__cleanup_and_return_null' free(...) may change the current 
errno.
- in 'label__free_cnt_data_and_exit' _nl_remove_locale and 
__libc_rwlock_unlock may reset the current errno.

All these problems were not added and exist in the current code.

---
Patch was tested on Ubuntu 24.04.

My 'LOCPATH' environment variable is "/snap/code/193/usr/lib/locale".

With an original (system) libc I have the following problem:

     #0 0x7d5c3aafc778 in realloc 
../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:85
     #1 0x7d5c394b05f2 in __argz_add_sep string/argz-addsep.c:34
     #2 0x7d5c39439bdb in __newlocale locale/newlocale.c:111
     #3 0x7d5c370e58fc (/lib/x86_64-linux-gnu/libp11-kit.so.0+0x1098fc) 
(BuildId: 1cbcd6ac7e0ff0259eb3acc14d556d2c1ec00cdc)
     #4 0x7d5c3b14571e in call_init elf/dl-init.c:74
     #5 0x7d5c3b145823 in call_init elf/dl-init.c:120
     #6 0x7d5c3b145823 in _dl_init elf/dl-init.c:121
     #7 0x7d5c3b15f59f  (/lib64/ld-linux-x86-64.so.2+0x1f59f) (BuildId: 
1c8db5f83bba514f8fd5f1fb6d7be975be1bb855)

SUMMARY: AddressSanitizer: 46 byte(s) leaked in 1 allocation(s).

When I run with my corrected libc, through runtest.sh <my-application>, 
all is OK.

Signed-off-by: Dmitry Kovalenko <d.kovalenko@postgrespro.ru>
-------------- next part --------------
From 70236429947b5685df66bddafdff2738daa7e960 Mon Sep 17 00:00:00 2001
From: "d.kovalenko" <d.kovalenko@postgrespro.ru>
Date: Sat, 17 May 2025 13:33:16 +0300
Subject: [PATCH] locale: memory leak in newlocale (BZ #25770)
To: libc-alpha@sourceware.org

It is a fix for memory leak in the function 'newlocale' (the real name is __newlocale).

There is not released a memory for the local buffer 'locale_path'.

https://sourceware.org/bugzilla/show_bug.cgi?id=25770

I made the minimal changes to avoid the unnecessary reformating/refactoring of source code.

Potentail problems:
- in 'label__cleanup_and_return_null' free(...) may change the current errno.
- in 'label__free_cnt_data_and_exit' _nl_remove_locale and __libc_rwlock_unlock may reset the current errno.

All these problems were not added and exist in the current code.

---
Patch was tested on Ubuntu 24.04.

My 'LOCPATH' environment variable is "/snap/code/193/usr/lib/locale".

With an original (system) libc I have the following problem:

    #0 0x7d5c3aafc778 in realloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:85
    #1 0x7d5c394b05f2 in __argz_add_sep string/argz-addsep.c:34
    #2 0x7d5c39439bdb in __newlocale locale/newlocale.c:111
    #3 0x7d5c370e58fc  (/lib/x86_64-linux-gnu/libp11-kit.so.0+0x1098fc) (BuildId: 1cbcd6ac7e0ff0259eb3acc14d556d2c1ec00cdc)
    #4 0x7d5c3b14571e in call_init elf/dl-init.c:74
    #5 0x7d5c3b145823 in call_init elf/dl-init.c:120
    #6 0x7d5c3b145823 in _dl_init elf/dl-init.c:121
    #7 0x7d5c3b15f59f  (/lib64/ld-linux-x86-64.so.2+0x1f59f) (BuildId: 1c8db5f83bba514f8fd5f1fb6d7be975be1bb855)

SUMMARY: AddressSanitizer: 46 byte(s) leaked in 1 allocation(s).

When I run with my corrected libc, through runtest.sh <my-application>, all is OK.

Signed-off-by: Dmitry Kovalenko <d.kovalenko@postgrespro.ru>
---
 locale/newlocale.c | 34 +++++++++++++++++++++++-----------
 1 file changed, 23 insertions(+), 11 deletions(-)

diff --git a/locale/newlocale.c b/locale/newlocale.c
index d25a6038d3..a6990c70e9 100644
--- a/locale/newlocale.c
+++ b/locale/newlocale.c
@@ -45,8 +45,6 @@ __newlocale (int category_mask, const char *locale, locale_t base)
   const char *newnames[__LC_LAST];
   struct __locale_struct result;
   locale_t result_ptr;
-  char *locale_path;
-  size_t locale_path_len;
   const char *locpath_var;
   int cnt;
   size_t names_len;
@@ -90,16 +88,18 @@ __newlocale (int category_mask, const char *locale, locale_t base)
 	return NULL;
       *result_ptr = result;
 
-      goto update;
+      goto label__update;
     }
 
+ {
   /* We perhaps really have to load some data.  So we determine the
      path in which to look for the data now.  The environment variable
      `LOCPATH' must only be used when the binary has no SUID or SGID
      bit set.  If using the default path, we tell _nl_find_locale
      by passing null and it can check the canonical locale archive.  */
-  locale_path = NULL;
-  locale_path_len = 0;
+
+  char *locale_path = NULL;
+  size_t locale_path_len = 0;
 
   locpath_var = getenv ("LOCPATH");
   if (locpath_var != NULL && locpath_var[0] != '\0')
@@ -135,7 +135,7 @@ __newlocale (int category_mask, const char *locale, locale_t base)
 
 	  if (cnt == __LC_LAST)
 	    /* Bogus category name.  */
-	    ERROR_RETURN;
+	    goto label__cleanup_and_error_return;
 
 	  /* Found the category this clause sets.  */
 	  specified_mask |= 1 << cnt;
@@ -154,7 +154,7 @@ __newlocale (int category_mask, const char *locale, locale_t base)
 
       if (category_mask &~ specified_mask)
 	/* The composite name did not specify all categories we need.  */
-	ERROR_RETURN;
+	goto label__cleanup_and_error_return;
     }
 
   /* Protect global data.  */
@@ -171,7 +171,7 @@ __newlocale (int category_mask, const char *locale, locale_t base)
 						   cnt, &newnames[cnt]);
 	  if (result.__locales[cnt] == NULL)
 	    {
-	    free_cnt_data_and_exit:
+	    label__free_cnt_data_and_exit:
 	      while (cnt-- > 0)
 		if (((category_mask & 1 << cnt) != 0)
 		    && result.__locales[cnt]->usage_count != UNDELETABLE)
@@ -180,7 +180,7 @@ __newlocale (int category_mask, const char *locale, locale_t base)
 
               /* Critical section left.  */
               __libc_rwlock_unlock (__libc_setlocale_lock);
-	      return NULL;
+	      goto label__cleanup_and_return_null;
 	    }
 
 	  if (newnames[cnt] != _nl_C_name)
@@ -200,7 +200,7 @@ __newlocale (int category_mask, const char *locale, locale_t base)
   if (result_ptr == NULL)
     {
       cnt = __LC_LAST;
-      goto free_cnt_data_and_exit;
+      goto label__free_cnt_data_and_exit;
     }
 
   if (base == NULL)
@@ -258,11 +258,23 @@ __newlocale (int category_mask, const char *locale, locale_t base)
       free (base);
     }
 
+  free(locale_path);
+
   /* Critical section left.  */
   __libc_rwlock_unlock (__libc_setlocale_lock);
+  goto label__update;
+
+ label__cleanup_and_error_return:
+  free(locale_path);
+  ERROR_RETURN;
+
+ label__cleanup_and_return_null:
+  free(locale_path);
+  return NULL;
+ }
 
   /* Update the special members.  */
- update:
+ label__update:
   {
     union locale_data_value *ctypes = result_ptr->__locales[LC_CTYPE]->values;
     result_ptr->__ctype_b = (const unsigned short int *)
-- 
2.46.0.dirty



More information about the Libc-alpha mailing list