]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
intl: Fix memory leak in _nl_find_domain on allocation failure
authorAvinal Kumar <avinal.xlvii@gmail.com>
Tue, 5 May 2026 11:28:25 +0000 (16:58 +0530)
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>
Mon, 18 May 2026 16:31:00 +0000 (13:31 -0300)
When _nl_explode_name() returns -1 (out of memory) and the locale was
resolved through an alias, _nl_find_domain() returns immediately
without freeing the locale copy allocated earlier.  Similarly,
when _nl_make_l10nflist() returns NULL, the 'goto out' skips the
alias_value free.

Fix by nesting the _nl_make_l10nflist() call and its result handling
inside 'if (mask != -1)' instead of returning early.  Move the
normalized_codeset free inside the same block.  Both failure paths
now fall through to the unconditional alias_value free at the end.

Imported from GNU gettext commit 10eafd9e5.
Original author: Bruno Haible <bruno@clisp.org>

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
intl/finddomain.c

index 7da0cce62f4e312d517bf671abac034e4b7947e5..c9153b8d8e88577392e4ab1a9fbf1b5013ea60dd 100644 (file)
@@ -50,7 +50,6 @@
 /* List of already loaded domains.  */
 static struct loaded_l10nfile *_nl_loaded_domains;
 
-
 /* Return a data structure describing the message catalog described by
    the DOMAINNAME and CATEGORY parameters with respect to the currently
    established bindings.  */
@@ -133,55 +132,52 @@ _nl_find_domain (const char *dirname, char *locale,
 
   /* Now we determine the single parts of the locale name.  First
      look for the language.  Termination symbols are `_', '.', and `@'.  */
-  mask = _nl_explode_name (locale, &language, &modifier, &territory,
-                          &codeset, &normalized_codeset);
-  if (mask == -1)
-    /* This means we are out of core.  */
-    return NULL;
-
-  /* We need to protect modifying the _NL_LOADED_DOMAINS data.  */
-  gl_rwlock_wrlock (lock);
-
-  /* Create all possible locale entries which might be interested in
-     generalization.  */
-  retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
-                              strlen (dirname) + 1, mask, language, territory,
-                              codeset, normalized_codeset, modifier,
-                              domainname, 1);
+  mask = _nl_explode_name (locale, &language, &modifier, &territory, &codeset,
+                          &normalized_codeset);
+  if (mask != -1) /* Not out of memory?  */
+    {
+      /* We need to protect modifying the _NL_LOADED_DOMAINS data.  */
+      gl_rwlock_wrlock (lock);
 
-  gl_rwlock_unlock (lock);
+      /* Create all possible locale entries which might be interested in
+         generalization.  */
+      retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
+                                  strlen (dirname) + 1, mask, language,
+                                  territory, codeset, normalized_codeset,
+                                  modifier, domainname, 1);
 
-  if (retval == NULL)
-    /* This means we are out of core.  */
-    goto out;
+      gl_rwlock_unlock (lock);
 
-  if (retval->decided <= 0)
-    _nl_load_domain (retval, domainbinding);
-  if (retval->data == NULL)
-    {
-      int cnt;
-      for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
+      if (retval != NULL) /* Not out of memory?  */
        {
-         if (retval->successor[cnt]->decided <= 0)
-           _nl_load_domain (retval->successor[cnt], domainbinding);
-         if (retval->successor[cnt]->data != NULL)
-           break;
+         if (retval->decided <= 0)
+           _nl_load_domain (retval, domainbinding);
+         if (retval->data == NULL)
+           {
+             int cnt;
+             for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
+               {
+                 if (retval->successor[cnt]->decided <= 0)
+                   _nl_load_domain (retval->successor[cnt], domainbinding);
+                 if (retval->successor[cnt]->data != NULL)
+                   break;
+               }
+           }
        }
+
+      /* The space for normalized_codeset is dynamically allocated.
+         Free it.  */
+      if (mask & XPG_NORM_CODESET)
+       free ((void *) normalized_codeset);
     }
 
   /* The room for an alias was dynamically allocated.  Free it now.  */
   if (alias_value != NULL)
     free (locale);
 
-out:
-  /* The space for normalized_codeset is dynamically allocated.  Free it.  */
-  if (mask & XPG_NORM_CODESET)
-    free ((void *) normalized_codeset);
-
   return retval;
 }
 
-
 #ifdef _LIBC
 /* This is called from iconv/gconv_db.c's free_mem, as locales must
    be freed before freeing gconv steps arrays.  */