php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #60689 Return $this double quoted __toString
Submitted: 2012-01-09 06:16 UTC Modified: 2012-01-17 19:19 UTC
Votes:3
Avg. Score:3.3 ± 0.5
Reproduced:3 of 3 (100.0%)
Same Version:1 (33.3%)
Same OS:0 (0.0%)
From: valentiny510 at yahoo dot es Assigned:
Status: Not a bug Package: Class/Object related
PHP Version: 5.3.8 OS: windows xp
Private report: No CVE-ID: None
 [2012-01-09 06:16 UTC] valentiny510 at yahoo dot es
Description:
------------
If you try to return $this double quoted in __toString break all the system and does not even throw any exception or log anything.

my php.ini have
error_reporting = E_ALL | E_STRICT
display_errors = On
display_startup_errors = On
log_errors = On
report_memleaks = On
report_zend_debug = 1


Test script:
---------------
class test
{
    public function __toString ( )
    {
        return "$this";
    }
}

$test = new test;
echo $test;


Expected result:
----------------
the string "$this" or the error exception

Actual result:
--------------
The server break completely and does not output nothing, not even the error log.
The browser say "The connection has been reset".
(The apache server is still logging the requests)

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-01-10 15:25 UTC] phpmpan at mpan dot pl
Confirmed for 5.3.8 and for 2011/01/04 snapshots of 5.3, 5.4 and trunk, on Arch64.
Inifnite recursion causing SIGSEGV. Partial backtrace:
----------- BEGIN BACKTRACE -----------
#0  0x00000000005d853e in zend_mm_search_large_block (heap=
Cannot access memory at address 0x7fffff7fefc0
) at src-trunk/Zend/zend_alloc.c:1804
#1  0x00000000005d8a50 in _zend_mm_alloc_int (heap=0xad72b0, size=32, 
    __zend_filename=0x86c908 "src-trunk/Zend/zend.c", 
    __zend_lineno=264, __zend_orig_filename=0x0, __zend_orig_lineno=0)
    at src-trunk/Zend/zend_alloc.c:1934
#2  0x00000000005da624 in _emalloc (size=32, 
    __zend_filename=0x86c908 "src-trunk/Zend/zend.c", 
    __zend_lineno=264, __zend_orig_filename=0x0, __zend_orig_lineno=0)
    at src-trunk/Zend/zend_alloc.c:2425
#3  0x000000000060dfd9 in zend_make_printable_zval (expr=0x7ffff6777618, 
    expr_copy=0x7fffff7ff220, use_copy=0x7fffff7ff21c)
    at src-trunk/Zend/zend.c:264
#4  0x0000000000712d75 in ZEND_ADD_VAR_SPEC_UNUSED_CV_HANDLER (
    execute_data=0x7ffff6710510)
    at src-trunk/Zend/zend_vm_execute.h:25868
#5  0x000000000064e123 in execute (op_array=0x7ffff7fcbf68)
    at src-trunk/Zend/zend_vm_execute.h:410
#6  0x00000000005fd1de in zend_call_function (fci=0x7fffff7ff6e0, 
    fci_cache=0x7fffff7ff670)
    at src-trunk/Zend/zend_execute_API.c:958
#7  0x000000000062edc1 in zend_call_method (object_pp=0x7fffff7ff798, 
    obj_ce=0x7ffff7fcab98, fn_proxy=0x7ffff7fcad00, function_name=0x872790 "__tostring", 
    function_name_len=10, retval_ptr_ptr=0x7fffff7ff7a8, param_count=0, arg1=0x0, 
    arg2=0x0) at src-trunk/Zend/zend_interfaces.c:97
#8  0x00000000006462e8 in zend_std_cast_object_tostring (readobj=0x7ffff6777618, 
    writeobj=0x7fffff7ff960, type=6)
    at src-trunk/Zend/zend_object_handlers.c:1494
#9  0x000000000060e0b4 in zend_make_printable_zval (expr=0x7ffff6801598, 
    expr_copy=0x7fffff7ff960, use_copy=0x7fffff7ff95c)
    at src-trunk/Zend/zend.c:267
#10 0x0000000000712d75 in ZEND_ADD_VAR_SPEC_UNUSED_CV_HANDLER (
    execute_data=0x7ffff6710428)
    at src-trunk/Zend/zend_vm_execute.h:25868
#11 0x000000000064e123 in execute (op_array=0x7ffff7fcbf68)
    at src-trunk/Zend/zend_vm_execute.h:410
#12 0x00000000005fd1de in zend_call_function (fci=0x7fffff7ffe20, 
    fci_cache=0x7fffff7ffdb0)
    at src-trunk/Zend/zend_execute_API.c:958
(#7-#12 repeats)
------------ END BACKTRACE ------------
 [2012-01-10 16:43 UTC] valentiny510 at yahoo dot es
Looking into the source (zend_object_handlers.c) you donĀ“t need the debugger:

ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type TSRMLS_DC) /* {{{ */
{
    zval *retval;
    zend_class_entry *ce;

    switch (type) {
        case IS_STRING:
            ce = Z_OBJCE_P(readobj);
            if (ce->__tostring &&
                (zend_call_method_with_0_params(&readobj, ce, &ce->__tostring, "__tostring", &retval) || EG(exception))) {
                if (EG(exception)) {
                    if (retval) {
                        zval_ptr_dtor(&retval);
                    }
                    zend_error(E_ERROR, "Method %s::__toString() must not throw an exception", ce->name);
                    return FAILURE;
                }
                if (Z_TYPE_P(retval) == IS_STRING) {
                    INIT_PZVAL(writeobj);
                    if (readobj == writeobj) {
                        zval_dtor(readobj);
                    }
                    ZVAL_ZVAL(writeobj, retval, 1, 1);
                    if (Z_TYPE_P(writeobj) != type) {
                        convert_to_explicit_type(writeobj, type);
                    }
                    return SUCCESS;
                } else {
                    zval_ptr_dtor(&retval);
                    INIT_PZVAL(writeobj);
                    if (readobj == writeobj) {
                        zval_dtor(readobj);
                    }
                    ZVAL_EMPTY_STRING(writeobj);
                    zend_error(E_RECOVERABLE_ERROR, "Method %s::__toString() must return a string value", ce->name);
                    return SUCCESS;
                }
            }
            return FAILURE;
        case IS_BOOL:
            INIT_PZVAL(writeobj);
            ZVAL_BOOL(writeobj, 1);
            return SUCCESS;
        case IS_LONG:
            ce = Z_OBJCE_P(readobj);
            zend_error(E_NOTICE, "Object of class %s could not be converted to int", ce->name);
            INIT_PZVAL(writeobj);
            if (readobj == writeobj) {
                zval_dtor(readobj);
            }
            ZVAL_LONG(writeobj, 1);
            return SUCCESS;
        case IS_DOUBLE:
            ce = Z_OBJCE_P(readobj);
            zend_error(E_NOTICE, "Object of class %s could not be converted to double", ce->name);
            INIT_PZVAL(writeobj);
            if (readobj == writeobj) {
                zval_dtor(readobj);
            }
            ZVAL_DOUBLE(writeobj, 1);
            return SUCCESS;
        default:
            INIT_PZVAL(writeobj);
            Z_TYPE_P(writeobj) = IS_NULL;
            break;
    }
    return FAILURE;
}
 [2012-01-17 19:19 UTC] mike@php.net
-Status: Open +Status: Bogus
 [2012-01-17 19:19 UTC] mike@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php


 [2012-01-18 09:51 UTC] harrygriff at griffcomp dot com
I just love when after one week somebody come and renspond with a copy/paste message saying is not a bug with no arguments.

My tests reveal that this script completely crush the apache server and dont't even warn about it. Apache logs show this line..
[Wed Jan 18 10:36:53 2012] [notice] Parent: child process exited with status 3221225477 -- Restarting.
.......etc...
After some time investigating I found this answer..
The 3221225477 is C0000005 in hex notation, which is just the good old general protection fault, caught by the windows kernel.

In other words: Apache itself, some Apace module or any of the DLLs used by Apache or its modules crashed. Probably because of some faulty C code using an invalid pointer.

So if only few lines of PHP code can crush/restart the server don't tell me is "not a bug"... You can blame apache or windows kernel but the point is the error started with an PHP script.. Please check this Mike before respond
 [2012-01-18 21:15 UTC] phpmpan at mpan dot pl
> Probably because of some faulty C code using an invalid pointer.
Nope. This is caused by faulty PHP code that valentiny510 presented us earlier. And it's not because of invalid pointer. As backtrace shows, it's stack overflow.

However I agree that setting this report to bogus without a reason is not a nice thing. `__toString` documentation says nothing about recursive calls. It only says that the return type should be a string or `E_RECOVERABLE_ERROR` happens. In this case this requirement is satisfied. Therefore what should we serach for in the manual?

While this is an obvious error in user's code, a similar situation in other functions does not cause a low-level crash. PHP nicely handles this and emits an error. But in this specific case this is not working. From PHP sources point of view it's obvious that different things handle those cases, but it's not so easy to see or expected from users point of view. Therefore this should be either fixed or, if fixing this would require too much work, ignoring. But ignoring with a specific reason of error being too hard to fix.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Oct 31 23:01:28 2024 UTC