[patch] Fix [BZ#14122] memory leak in nss_parse_service_list

Andreas Jaeger aj@suse.com
Tue May 22 19:47:00 GMT 2012


On 05/21/2012 07:13 AM, Paul Pluzhnikov wrote:
> On Sun, May 20, 2012 at 4:22 PM, Paul Pluzhnikov<ppluzhnikov@google.com>  wrote:
>
>> Here is a rather simple fix for [BZ#14122]: memory leak in
>> nss_parse_service_list.
>
> That patch fixed leaks in the error paths, but did not fix the original problem.
>
> Attached is an updated patch that fixes both.
>
> Re-tested on Linux/x86_64 an i686.
>
> Thanks,
> --
> Paul Pluzhnikov
>
>
> 2012-05-20  Paul Pluzhnikov<ppluzhnikov@google.com>
>
>          [BZ #14122]
>          * nss/nsswitch.c (defconfig_entries): New variable.
>          (__nss_database_lookup): Don't leak defconfig entries.
>          (nss_parse_service_list): Don't leak on error paths.
>          (free_database_entries): New function.
>          (free_defconfig): New function.
>          (free_mem): Move common code to free_database_entries.

> diff --git a/nss/nsswitch.c b/nss/nsswitch.c
> index 53ff5f8..3614856 100644
> --- a/nss/nsswitch.c
> +++ b/nss/nsswitch.c
> @@ -86,6 +86,9 @@ static const char *const __nss_shlib_revision = LIBNSS_FILES_SO + 15;
>  /* The root of the whole data base.  */
>  static name_database *service_table;
>
> +/* defconfig entries.  */

Please give a more descriptive comment. What about
List of default service lists that are generated by glibc since 
/etc/nsswitch does not provide a value. The list is only allocated for 
freeing it at the end.

> +static name_database_entry *defconfig_entries;
> +
>
>  /* Nonzero if this is the nscd process.  */
>  static bool is_nscd;
> @@ -141,8 +144,25 @@ __nss_database_lookup (const char *database, const char *alternate_name,
>       DEFCONFIG specifies the default service list for this database,
>       or null to use the most common default.  */
>    if (*ni == NULL)
> -    *ni = nss_parse_service_list (defconfig
> -				  ?: "nis [NOTFOUND=return] files");
> +    {
> +      *ni = nss_parse_service_list (defconfig
> +				    ?: "nis [NOTFOUND=return] files");
> +      if (*ni != NULL)
> +	{
> +	  /* Don't leak the memory we've just allocated.  */
> +	  name_database_entry *entry;
> +
Add a comment for the +1, e.g.:
Allocate ENTRY plus size of name (1 here)

> +	  entry = (name_database_entry *) malloc (sizeof (*entry) + 1);
> +
> +	  if (entry != NULL)
> +	    {
> +	      entry->next = defconfig_entries;
> +	      entry->service = *ni;
> +	      entry->name[0] = '\0';
> +	      defconfig_entries = entry;
> +	    }
> +	}
> +    }
>
>    __libc_lock_unlock (lock);
>
> @@ -644,7 +664,7 @@ nss_parse_service_list (const char *line)
>  		  else if (__strncasecmp (name, "UNAVAIL", 7) == 0)
>  		    status = NSS_STATUS_UNAVAIL;
>  		  else
> -		    return result;
> +		    goto finish;
>  		}
>  	      else if (line - name == 8)
>  		{
> @@ -653,15 +673,15 @@ nss_parse_service_list (const char *line)
>  		  else if (__strncasecmp (name, "TRYAGAIN", 8) == 0)
>  		    status = NSS_STATUS_TRYAGAIN;
>  		  else
> -		    return result;
> +		    goto finish;
>  		}
>  	      else
> -		return result;
> +		goto finish;
>
>  	      while (isspace (line[0]))
>  		++line;
>  	      if (line[0] != '=')
> -		return result;
> +		goto finish;
>  	      do
>  		++line;
>  	      while (isspace (line[0]));
> @@ -677,7 +697,7 @@ nss_parse_service_list (const char *line)
>  		       && __strncasecmp (name, "CONTINUE", 8) == 0)
>  		action = NSS_ACTION_CONTINUE;
>  	      else
> -		return result;
> +		goto finish;
>
>  	      if (not)
>  		{
> @@ -705,6 +725,11 @@ nss_parse_service_list (const char *line)
>
>        *nextp = new_service;
>        nextp = &new_service->next;
> +      continue;
> +
> +    finish:
> +      free (new_service);
> +      return result;
>      }
>  }
>
> @@ -816,22 +841,9 @@ __nss_disable_nscd (void (*cb) (size_t, struct traced_file *))
>  }
>  #endif
>
> -
> -/* Free all resources if necessary.  */
> -libc_freeres_fn (free_mem)
> +static void
> +free_database_entries (name_database_entry *entry)
>  {
> -  name_database *top = service_table;
> -  name_database_entry *entry;
> -  service_library *library;
> -
> -  if (top == NULL)
> -    /* Maybe we have not read the nsswitch.conf file.  */
> -    return;
> -
> -  /* Don't disturb ongoing other threads (if there are any).  */
> -  service_table = NULL;
> -
> -  entry = top->entry;
>    while (entry != NULL)
>      {
>        name_database_entry *olde = entry;
> @@ -851,6 +863,36 @@ libc_freeres_fn (free_mem)
>        entry = entry->next;
>        free (olde);
>      }
> +}
> +
> +/* Free all resources if necessary.  */
> +libc_freeres_fn (free_defconfig)
> +{
> +  name_database_entry *entry = defconfig_entries;
> +
> +  if (entry == NULL)
> +    /* defconfig was not used.  */
> +    return;
> +
> +  /* Don't disturb ongoing other threads (if there are any).  */
> +  defconfig_entries = NULL;
> +
> +  free_database_entries (entry);
> +}
> +
> +libc_freeres_fn (free_mem)
> +{
> +  name_database *top = service_table;
> +  service_library *library;
> +
> +  if (top == NULL)
> +    /* Maybe we have not read the nsswitch.conf file.  */
> +    return;
> +
> +  /* Don't disturb ongoing other threads (if there are any).  */
> +  service_table = NULL;
> +
> +  free_database_entries (top->entry);
>
>    library = top->library;
>    while (library != NULL)

The patch is fine, please add some comments as proposed and commit,
While you're add it, change the first two lines as well:

-/* Copyright (C) 1996-2012
-   Free Software Foundation, Inc.
+/* Copyright (C) 1996-2012 Free Software Foundation, Inc.

thanks,
Andreas
-- 
  Andreas Jaeger aj@{suse.com,opensuse.org} Twitter/Identica: jaegerandi
   SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
    GF: Jeff Hawn,Jennifer Guild,Felix Imendörffer,HRB16746 (AG Nürnberg)
     GPG fingerprint = 93A3 365E CE47 B889 DF7F  FED1 389A 563C C272 A126



More information about the Libc-alpha mailing list