For [BZ#14122], plug memory leaks in nsswitch.c.
authorPaul Pluzhnikov <ppluzhnikov@google.com>
Tue, 22 May 2012 20:09:27 +0000 (13:09 -0700)
committerPaul Pluzhnikov <ppluzhnikov@google.com>
Tue, 22 May 2012 20:09:27 +0000 (13:09 -0700)
ChangeLog
nss/nsswitch.c

index 7b4157079fa2dacfeca0ff53cbf6b4b9a361ab24..159f4f2e93b333b94fa3e9b7f049a56fb0aa8a9e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2012-05-22  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.
+
 2012-05-22  H.J. Lu  <hongjiu.lu@intel.com>
 
        * sysdeps/unix/sysv/linux/x86_64/x32/Makefile (sysdep_routines):
index 53ff5f8f714d72c267847ede662b99aa6507034d..464f478d2b7f5c90aae2a0e54ff0a9618a2ef2f1 100644 (file)
@@ -1,5 +1,4 @@
-/* Copyright (C) 1996-2012
-   Free Software Foundation, Inc.
+/* Copyright (C) 1996-2012 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
 
@@ -86,6 +85,12 @@ static const char *const __nss_shlib_revision = LIBNSS_FILES_SO + 15;
 /* The root of the whole data base.  */
 static name_database *service_table;
 
+/* List of default service lists that were generated by glibc because
+   /etc/nsswitch.conf did not provide a value.
+   The list is only maintained so we can free such service lists in
+   __libc_freeres.  */
+static name_database_entry *defconfig_entries;
+
 
 /* Nonzero if this is the nscd process.  */
 static bool is_nscd;
@@ -141,8 +146,27 @@ __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)
+       {
+         /* Record the memory we've just allocated in defconfig_entries list,
+            so we can free it later.  */
+         name_database_entry *entry;
+
+         /* 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 +668,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 +677,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 +701,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 +729,11 @@ nss_parse_service_list (const char *line)
 
       *nextp = new_service;
       nextp = &new_service->next;
+      continue;
+
+    finish:
+      free (new_service);
+      return result;
     }
 }
 
@@ -816,22 +845,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 +867,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)