about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog10
-rw-r--r--nss/nsswitch.c94
2 files changed, 80 insertions, 24 deletions
diff --git a/ChangeLog b/ChangeLog
index 7b4157079f..159f4f2e93 100644
--- 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):
diff --git a/nss/nsswitch.c b/nss/nsswitch.c
index 53ff5f8f71..464f478d2b 100644
--- a/nss/nsswitch.c
+++ b/nss/nsswitch.c
@@ -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)