lists.arthurdejong.org
RSS feed

[nssldap] libnss_ldap leaks memory - causes nscd to grow

[Date Prev][Date Next] [Thread Prev][Thread Next]

[nssldap] libnss_ldap leaks memory - causes nscd to grow



The following was submitted as bugs to Ubuntu:
  https://bugs.launchpad.net/ubuntu/+source/libnss-ldap/+bug/292971
and original source developers:
  http://bugzilla.padl.com/show_bug.cgi?id=418

Using valgrind with nscd might also be of interest to others.


A memory leak in libnss-ldap over time can cause the nscd
process to grow extremely large. For example one nscd
process that had been running for three months was using
6GB of swap!

The problem is in the original Padl nss-ldap in at least versions
258, 261 and 265. Ubuntu Hardy uses 258, Karmic uses 261, and
the Padl current release is 265.


The ldap-nss.c do_init() may be called more then once,
to initialize an ldap session  and save the session in
in __session.ls_conn and set the __session.ls_stat = LD_INITIALIZED
But it does not check the state  to see if has be initialized,
and at line 1239: __session.ls_conn = NULL;

The attached patch to to the libnss-ldap_261-2.1Ubuntu4 fixes
the problem, by testing __session.ls_stat == LD_INITIALIZED

The ldap-nss.c also has a patch to call do_close twice that
I had previously turned it to Padl and is now in 265.

For testing purposes, the patch also adds atexit(do_atexit);
and a do_atexit routine to call do_close. This will then cause
the last session to be be freed. This make it much easier to
use valgrind to check for memory leaks. (in the nscd.c in lib6c
the _exit was change to exit so the atexit would be called.)

Debug versions of nscd and libnss-ldap where created, and
and used with valgrind to track down the memory leaks.
Attached is a script used with valgrind. The LD_PRELOAD was
needed so dynamic libs would not be unloaded, and valgrind
could find the symbol tables.

--

   Douglas E. Engert  <DEEngert@anl.gov>
   Argonne National Laboratory
   9700 South Cass Avenue
   Argonne, Illinois  60439
   (630) 252-5444


--- libc6/eglibc-2.10.1/nscd/,nscd.c	2009-07-29 11:29:54.000000000 -0500
+++ libc6/eglibc-2.10.1/nscd/nscd.c	2010-03-15 15:02:53.429225933 -0500
@@ -491,7 +491,9 @@
 	msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
     }
 
-  _exit (EXIT_SUCCESS);
+/* for testing to let libnss-ldap cleanup, we use exit */
+	exit (EXIT_SUCCESS);
+//  _exit (EXIT_SUCCESS);
 }
 
 /* Returns 1 if the process in pid file FILE is running, 0 if not.  */


--- libnss-ldap/libnss-ldap-261/build-tree/nss_ldap-261/,ldap-nss.c	2010-03-15 10:04:47.593727549 -0500
+++ libnss-ldap/libnss-ldap-261/build-tree/nss_ldap-261/ldap-nss.c	2010-03-17 16:04:16.165226755 -0500
@@ -184,6 +184,7 @@
 static void do_atfork_setup (void);
 #endif
 
+static void do_atexit (void);  /* allow exit to cleanup to help valgrind */
 /*
  * Close the global session, sending an unbind.
  */
@@ -553,11 +554,25 @@
   (void) __libc_atfork (do_atfork_prepare, do_atfork_parent, do_atfork_child);
 #endif
 
+atexit(do_atexit); /* allow exit to cleanup to help valgrind */
+
   debug ("<== do_atfork_setup");
 }
 #endif
 
 /*
+ * allow exit to cleanup to help valgrind
+ */
+void
+do_atexit (void)
+{
+  debug ("<== do_atexit");
+  _nss_ldap_enter();
+  do_close();
+  debug ("==> do_atexit (should be no more activity)");
+}
+
+/*
  * Acquires global lock, blocks SIGPIPE.
  */
 void
@@ -1107,6 +1122,7 @@
 
   debug ("==> do_init");
 
+
   if (_nss_ldap_validateconfig (__config) != NSS_SUCCESS)
     {
       do_close ();
@@ -1236,9 +1252,9 @@
 	}
     }
 
-  __session.ls_conn = NULL;
+/* LOOKS LIKE A PROBLEM. COULD BE INITIALIZED, BUT NOT CONNECTED */
+  if (__session.ls_state == LS_UNINITIALIZED) {
   __session.ls_timestamp = 0;
-  __session.ls_state = LS_UNINITIALIZED;
 
 #if defined(HAVE_PTHREAD_ONCE) && defined(HAVE_PTHREAD_ATFORK)
   if (pthread_once (&__once, do_atfork_setup) != 0)
@@ -1357,6 +1373,10 @@
   __session.ls_state = LS_INITIALIZED;
 
   debug ("<== do_init (initialized session)");
+  } /* if already initialized  but not connected */
+  else {
+    debug ("<== do_init (already initialized)");
+  }
 
   return NSS_SUCCESS;
 }
@@ -1577,6 +1597,7 @@
 	}
       else
 	{
+          syslog(LOG_ERR, "nss-ldap: do_open: do_start_tls failed:stat=%d", stat);
 	  do_close ();
 	  debug ("<== do_open (TLS startup failed)");
 	  return stat;
@@ -2472,6 +2493,7 @@
 #endif /* LDAP_OPT_ERROR_NUMBER */
 	  syslog (LOG_AUTHPRIV | LOG_ERR, "nss_ldap: could not get LDAP result - %s",
 		  ldap_err2string (rc));
+          do_close();
 	  stat = NSS_UNAVAIL;
 	  break;
 	case LDAP_RES_SEARCH_ENTRY:
@@ -2507,6 +2529,7 @@
 		  syslog (LOG_AUTHPRIV | LOG_ERR,
 			  "nss_ldap: could not get LDAP result - %s",
 			  ldap_err2string (rc));
+                  do_close();
 		}
 	      else if (resultControls != NULL)
 		{


#   #remove this# !/bin/bash
# Above line was modified to would make it via e-mail filters
# that wont allow schee scripts 
#
# my options
#

LOG=/tmp/valgrind.nscd.exit.$$
echo log=$LOG
LOG2=/tmp/valgrind.nscd.exit.stderr.$$

export LD_LIBRARY_PATH=/usr/lib/debug/lib

LD_PRELOAD=/lib/libnss_ldap-2.10.1.so:/lib/tls/i686/cmov/libnss_compat-2.10.1.so:/lib/tls/i686/cmov/libnss_files-2.10.1.so:/lib/tls/i686/cmov/libnss_dns-2.10.1.so
 \
/usr/bin/valgrind -v --log-file=$LOG \
        --leak-check=full --leak-resolution=high \
        --show-reachable=yes \
        --num-callers=40 --track-origins=yes\
 /home/rootdee/source/libc6/eglibc-2.10.1/build-tree/i386-libc/nscd/nscd\
 2> $LOG2