php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #43450 Memory leak on some functions with implicit object __toString() call
Submitted: 2007-11-29 14:59 UTC Modified: 2007-12-22 23:09 UTC
Votes:7
Avg. Score:5.0 ± 0.0
Reproduced:7 of 7 (100.0%)
Same Version:6 (85.7%)
Same OS:6 (85.7%)
From: mfischer@php.net Assigned: davidc (profile)
Status: Closed Package: Scripting Engine problem
PHP Version: 5.2.5 OS: Any
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: mfischer@php.net
New email:
PHP Version: OS:

 

 [2007-11-29 14:59 UTC] mfischer@php.net
Description:
------------
Under certain circumenstances, the implicit call to __toString() on an object may lead to memory leaks.

In the reproducable example, the following line leaks ($o is a simply object):
 md5($o);
But this line doesn't:
 md5($o->__toString());

This only applies to certain functions, I've identifier md5, sha1 and crc32.

If I try other examples like strstr or strlen, there's no leak at all.

A wild guess is that this maybe has to do whether the function internally uses zend_parse_parameters() or zend_get_parameters_ex().

The function which leak use zend_parse_parameters(), the others don't.

But this may completely accidental.

It seems very related to bug#38591. However I don't see how bug#38604 is related to this issue (mentioned in bug#38591).

This leak was most notable found in an application which is supposed to run for a long time, even hours. So usually within web application this is not an issue.

Reproduce code:
---------------
<?php
    class Foo {
        function __toString() {
            return 'foo';
        }
    }
    for ($i = 0; $i < 1e5; $i++) {

        $o = new Foo;

        # leaks
        md5($o);
        # does not leak
        #md5($o->__toString());

        # does not leak either way
        # strstr($o, 'f');
        #strstr($o->__toString(), 'f');

        if ($i % 1e3 == 0) {
            printf("%u: %1.2f KB\n",
                $i, memory_get_usage(true) / 1024);
        }
    }


Expected result:
----------------
Constant memory usage.

Actual result:
--------------
Memory grows and grows.

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2007-11-30 01:34 UTC] mfischer@php.net
I'm still not sure if this has anything to do with the new Zend parsing API, but I've tested the md5 function with the zend_get_parameters_ex (the old API) and the leak didn't occur. See the two version for a comparison.


-------------------- currently --------------------
PHP_NAMED_FUNCTION(php_if_md5)
{
    char *arg;
    int arg_len;
    zend_bool raw_output = 0;
    char md5str[33];
    PHP_MD5_CTX context;
    unsigned char digest[16];

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
		"s|b", &arg, &arg_len, &raw_output) == FAILURE) {
        return;
    }

    md5str[0] = '\0';
    PHP_MD5Init(&context);
    PHP_MD5Update(&context, arg, arg_len);
    PHP_MD5Final(digest, &context);
    if (raw_output) {
        RETURN_STRINGL(digest, 16, 1);
    } else {
        make_digest_ex(md5str, digest, 16);
        RETVAL_STRING(md5str, 1);
    }

}



----------- hacked rewrite ------------------------
PHP_NAMED_FUNCTION(php_if_md5)
{
    zval **zarg;
    zend_bool raw_output = 0;
    char md5str[33];
    PHP_MD5_CTX context;
    unsigned char digest[16];


    if (ZEND_NUM_ARGS() != 1 ||
		zend_get_parameters_ex(1, &zarg) == FAILURE) {
        WRONG_PARAM_COUNT;
    }
    convert_to_string_ex(zarg);

    md5str[0] = '\0';
    PHP_MD5Init(&context);
    PHP_MD5Update(&context, Z_STRVAL_PP(zarg), Z_STRLEN_PP(zarg));
    PHP_MD5Final(digest, &context);
    if (raw_output) {
        RETURN_STRINGL(digest, 16, 1);
    } else {
        make_digest_ex(md5str, digest, 16);
        RETVAL_STRING(md5str, 1);
    }
}

 [2007-12-20 11:47 UTC] helly@php.net
It appears that zend_std_cast_object_tostring() does not check whether it has to dref readobj prior writing to writeobj in case they are the same zval.

That said the code needs to be refactored to:
- if (readobj==writeobj) { zval_dtor(readobj); }  // not zval_ptr_dtor
- call INIT_PZVAL(writeobj) always
- set Z_TYPE_P(writeobj) = IS_NULL; for the default case
 [2007-12-21 16:37 UTC] davidc@php.net
Patches are added for 5_2, 5_3 and HEAD there:

http://dev.agoraproduction.com/php/Zend/

Thanks,
 [2007-12-22 18:45 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.


 [2012-02-26 14:56 UTC] laruence@php.net
Automatic comment from SVN on behalf of laruence
Revision: http://svn.php.net/viewvc/?view=revision&amp;revision=323563
Log: Improve fix for #61165, the previous one cause #43450 test failed
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Dec 03 17:01:29 2024 UTC