php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #41127 Memory leak in ldap_{first|next}_attribute functions
Submitted: 2007-04-17 22:26 UTC Modified: 2007-07-13 01:24 UTC
From: ktk at bigfoot dot com Assigned: jani
Status: Closed Package: LDAP related
PHP Version: 5.2.1 OS: Slackware GNU/Linux
Private report: No CVE-ID:
 [2007-04-17 22:26 UTC] ktk at bigfoot dot com
Description:
------------
PHP "leaks" memory in the ldap_{first|next}_attribute() functions.  Interestingly, the ldap_get_attributes() function does not leak.
I put "leaks" in quotes because the leak() function does not detect the issue; presumably, the memory is programmatically freed, just not until PHP exits.

If one does something like:
while (true) {
  $s = ldap_search($link, $base, $filter, $atts);
  for ($e = ldap_first_entry($link, $s); $e != false;
       $e = ldap_next_entry($link, $e)) {
    for ($a = ldap_first_attribute($link, $e, $ber); $a;
         $a = ldap_next_attribute($link, $e, $ber)) {  }
  }
  ldap_free_result($s);
}
then PHP will eat all available memory and bomb with a memory allocation error.  Unbinding and re-binding has no effect.

Reproduce code:
---------------
A working 51-line script that shows memory allocation versus iteration count can be found at this url:
http://enterprise.bidmc.harvard.edu/private/php-bugs/ldap-leak-test.php

There are a few variables you can set/tweak at the top to get it to point at whatever LDAP server you have handy.  Choosing a search filter that returns a dozen or so entries is ideal for showing the runaway memory.  You can also change the "$fail" variable from True to False, which will stop the leak by way of using the roughly-equivalent function ldap_get_attributes().

Expected result:
----------------
PHP's memory subsystem should collect and re-use the freed memory.  After some number of iterations of the test script (typically 100 to 500) the results reported by memory_get_usage() should remain stable.

Actual result:
--------------
The results reported by memory_get_usage() increase fairly linearly with each iteration until memory_limit is reached, whence the script bombs.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2007-04-18 01:01 UTC] cardoe@php.net
The problem is that ldap_first_attribute is defined as follows:

char *ldap_first_attribute(LDAP *ld, LDAPMessage *entry, BerElement **berptr )

And you have to pass berptr back to each ldap_next_attribute() call you make. So the ldap extension registers the berptr as a resource since there is no native PHP type for it. AFAIK there's no way to garbage collect resources once their reference in PHP is gone.

The memory is not actually leaking, it's just creating a bunch of resources. The only suggestion I have is to create a function ldap_free_attribute() which can free the passed in resource.

This issue is similar to the mysql issue when you don't free the results from mysql_query() operations and they just keep allocating resources...
 [2007-04-18 07:55 UTC] tony2001@php.net
>AFAIK there's no way to garbage collect resources once their
>reference in PHP is gone.
Surely there is. Almost all resources have their destructors which are called when the last reference is gone.
But le_ber_entry is missing a destructor:
le_ber_entry = zend_register_list_destructors_ex(NULL <-- no dtor , NULL, "ldap ber entry", module_number);

As far as I understand, this resource depends on both link and entry resources, so you should keep a reference to them and increase their reference count when creating and decrease it when destroying.
 [2007-04-23 15:22 UTC] cardoe@php.net
tony: I had written a patch like that before I posted my previous comment and it didn't appear to work. I guess I'll try to play with it some more. Got a reference extension I could peek at any chance?
 [2007-07-13 01:24 UTC] jani@php.net
This bug has been fixed in CVS.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.


 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Thu Apr 24 21:01:55 2014 UTC