Bug #3285
memory leaks in libsldap
100%
Description
'ldap_cachemgr' can allocate whole memory if LDAP server is not available. This can be reproduced with this configuration:
# ldapclient list NS_LDAP_FILE_VERSION= 2.0 NS_LDAP_BINDDN= cn=root,dc=ourdomain,dc=com NS_LDAP_BINDPASSWD= {NS1}xxxxxxxxxxx NS_LDAP_SERVERS= 10.28.73.199 NS_LDAP_SEARCH_BASEDN= dc=ourdomain,dc=com NS_LDAP_AUTH= simple NS_LDAP_SEARCH_REF= FALSE NS_LDAP_CACHETTL= 0 NS_LDAP_CREDENTIAL_LEVEL= proxy anonymous NS_LDAP_SERVICE_SEARCH_DESC= passwd:ou=users, dc=ourdomain,dc=com NS_LDAP_SERVICE_SEARCH_DESC= group:cn=groups, dc=ourdomain,dc=com NS_LDAP_SERVICE_SEARCH_DESC= netgroup:cn=netgroup, dc=ourdomain,dc=com
The first set of leaks is related to having several credential levels. This can be observed with ::findleaks in mdb:
# mdb -p 7888 Loading modules: [ ld.so.1 libumem.so.1 libc.so.1 libuutil.so.1 libnvpair.so.1 ] > ::find findleaks findstack > ::findleaks -vd findleaks: maximum buffers => 448 findleaks: actual buffers => 349 findleaks: findleaks: potential pointers => 179760 findleaks: dismissals => 147542 (82.0%) findleaks: misses => 26756 (14.8%) findleaks: dups => 5131 ( 2.8%) findleaks: follows => 331 ( 0.1%) findleaks: findleaks: peak memory usage => 62 kB findleaks: elapsed CPU time => 0.0 seconds findleaks: elapsed wall time => 0.0 seconds findleaks: BYTES LEAKED VMEM_SEG CALLER 24576 2 fe68d000 MMAP 4096 1 fec12000 MMAP ------------------------------------------------------------------------ Total 2 oversized leaks, 28672 bytes CACHE LEAKED BUFCTL CALLER 0808b810 8 080bdeb8 libc_hwcap1.so.1`strdup+0x26 08088c10 8 080969a0 libsldap.so.1`doSimpleBind+0x2d3 ------------------------------------------------------------------------ Total 16 buffers, 832 bytes mmap(2) leak: [fe68d000, fe693000), 24576 bytes mmap(2) leak: [fec12000, fec13000), 4096 bytes umem_alloc_80 leak: 8 buffers, 80 bytes each, 640 bytes total ADDR BUFADDR TIMESTAMP THREAD CACHE LASTLOG CONTENTS 80bdeb8 80baf08 55e582aa9fa 7 808b810 8080384 0 libumem.so.1`umem_cache_alloc_debug+0x144 libumem.so.1`umem_cache_alloc+0x153 libumem.so.1`umem_alloc+0xcd libumem.so.1`malloc+0x2a libc_hwcap1.so.1`strdup+0x26 libsldap.so.1`doSimpleBind+0x2ee libsldap.so.1`performBind+0x63 libsldap.so.1`openConnection+0x27d libsldap.so.1`makeConnection+0x6a0 libsldap.so.1`getConnection+0x4c3 libsldap.so.1`__s_api_getConnection+0x34 libsldap.so.1`__ns_ldap_getRootDSE+0xcc getldap_get_rootDSE+0xe8 libc_hwcap1.so.1`_thrp_setup+0x9b libc_hwcap1.so.1`_lwp_start umem_alloc_24 leak: 8 buffers, 24 bytes each, 192 bytes total ADDR BUFADDR TIMESTAMP THREAD CACHE LASTLOG CONTENTS 80969a0 80bebc0 55e582aa699 7 8088c10 8080320 0 libumem.so.1`umem_cache_alloc_debug+0x144 libumem.so.1`umem_cache_alloc+0x153 libumem.so.1`umem_alloc+0xcd libumem.so.1`malloc+0x2a libumem.so.1`calloc+0x4a libsldap.so.1`doSimpleBind+0x2d3 libsldap.so.1`performBind+0x63 libsldap.so.1`openConnection+0x27d libsldap.so.1`makeConnection+0x6a0 libsldap.so.1`getConnection+0x4c3 libsldap.so.1`__s_api_getConnection+0x34 libsldap.so.1`__ns_ldap_getRootDSE+0xcc getldap_get_rootDSE+0xe8 libc_hwcap1.so.1`_thrp_setup+0x9b libc_hwcap1.so.1`_lwp_start
getConnection() will call makeConnection() repeatedly, trying the different credential levels and bindings etc until one call finally succeeds. Each failing call to makeConnection() allocates an error structure (MKERROR), which is then leaked when *errorp is overwritten in the next call to makeConnection(). By calling __ns_ldap_freeError() in makeConnection() instead of just setting *errorp=NULL the leaks can be avoided.
The second leak was harder to find. On success, makeConnection() creates a connection structure and adds it to some cache by calling addConnection(). Normally this stuff is freed by calling DropConnection() in __ns_ldap_getRootDSE() before the connection thread exits. This call to DropConnection() is missing in the error path for ldap_search_ext_s() in __ns_ldap_getRootDSE().
This means that if makeConnection() succeeds (like it seems to do without actually connecting to any server in the case of an anonymous LDAP bind), but actually talking to the LDAP server fails, the connection information structure remains in the cache forever. This also means that a reference to that information is kept and ::findleaks can't find the leak.
Updated by Hans Rosenfeld over 8 years ago
webrev: http://grumpf.hope-2000.org/illumos-3285-webrev/
While at it I also added a few newlines to a bunch of debugging printfs that needed them.