[PATCH] intl: Fix memory leak in _nl_find_domain on allocation failure

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Wed May 13 15:45:03 GMT 2026



On 05/05/26 08:28, Avinal Kumar wrote:
> 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>


LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
> This is the second patch syncing glibc with gettext.
> 
>  intl/finddomain.c | 68 ++++++++++++++++++++++-------------------------
>  1 file changed, 32 insertions(+), 36 deletions(-)
> 
> diff --git a/intl/finddomain.c b/intl/finddomain.c
> index 7da0cce62f..c9153b8d8e 100644
> --- a/intl/finddomain.c
> +++ b/intl/finddomain.c
> @@ -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.  */



More information about the Libc-alpha mailing list