Bug #72854 [Opn->Csd]: PHP Crashes on duplicate destructor call

From: Date: Tue, 16 Aug 2016 19:07:00 +0000
Subject: Bug #72854 [Opn->Csd]: PHP Crashes on duplicate destructor call
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-203327@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=72854&edit=1 ID: 72854 Updated by: nikic@php.net Reported by: php at abiusx dot com Summary: PHP Crashes on duplicate destructor call -Status: Open +Status: Closed Type: Bug Package: Class/Object related Operating System: Mac OS X, Linux PHP Version: 7.0.9 Block user comment: N Private report: N New Comment: Automatic comment on behalf of nikic Revision: http://git.php.net/?p=php-src.git;a=commit;h=e2230c17d3e17981c739cb858bc78d47d2365836 Log: Fix bug #72854 Previous Comments: ------------------------------------------------------------------------ [2016-08-16 18:37:16] php at abiusx dot com Your sample code is not segfaulting for me. Plus, it has a NOTICE. ------------------------------------------------------------------------ [2016-08-16 18:35:53] php at abiusx dot com It sounds reasonable. All segfaults might be due to the same bug. To cause the other segfaults, please comment out wpdb destruct method entirely and rerun, but I highly suspect they are all of the same type. I faced a few and they behaved very similarly. ------------------------------------------------------------------------ [2016-08-16 18:09:22] nikic@php.net Reproduce script for at least one issue: <?php function get() { $t = new stdClass; $t->prop = $t; return $t; } $i = 42; get($t)->prop =& $i; The problem here is that get($t)->prop =& $i destroys the stdClass object, but that is also the object to which the assignment happens, so we end up writing a destroyed object. This can be avoided by using an intermediate garbage zval in assign_to_reference. Doing so also fixes the segfault (I only see the one, with and without --diehard) on your test case. ------------------------------------------------------------------------ [2016-08-16 16:18:02] php at abiusx dot com Interesting, though the last time I had the error the apply_context (and context-related functionality) did not exist. The happens when a descructor of a deep copied object is called from another destructor. The emulator uses __destructor on EmulatorObject, which is called by PHP when the object is no longer used, to relay the destructor call to emulator and respective object. A lot of code happens under the EmulatorObject::__destructor and AFAIK PHP has a sensitive context inside destructors (many previous bugs?). Refcount is 0 there, but I doubt that the cause is there... ------------------------------------------------------------------------ [2016-08-16 16:14:52] nikic@php.net Yes. The valgrind warnings happen right after ----- (19.6) > ✔ Running wpdb::__destruct()... --------- (19.6.1) > ✔ Found direct method wpdb::__destruct()... --------------- (19.6.1.1.1) > ✔ Setting class to 'wpdb' and self to 'wpdb'... And the top of zbacktrace at the time of the warning is: [0x1019cc30] Emulator->context_apply(reference) /home/nikic/php-analyzer/php-emul/emulator-functions.php:116 [0x1019cb30] Emulator->context_switch(object[0x1019cb80]) /home/nikic/php-analyzer/php-emul/emulator-functions.php:122 [0x1019c7c0] Emulator->run_function(object[0x1019c810], array(0)[0x1019c820], object[0x1019c830], array(4)[0x1019c840]) /home/nikic/php-analyzer/php-emul/emulator-functions.php:158 [0x1019c0b0] OOEmulator->run_user_static_method("wpdb", "__destruct", array(0)[0x1019c120], reference) /home/nikic/php-analyzer/php-emul/oo-methods.php:283 [0x1019bf90] OOEmulator->run_user_method(reference, "__destruct", array(0)[0x1019c000], "wpdb") /home/nikic/php-analyzer/php-emul/oo-methods.php:392 [0x1019be70] OOEmulator->run_method(reference, "__destruct") /home/nikic/php-analyzer/php-emul/oo-methods.php:368 [0x1019bd20] EmulatorObject->__destruct() /home/nikic/php-analyzer/php-emul/oo.php:73 The context_apply() method is private function context_apply(EmulatorExecutionContext $context) { $bu_context=new EmulatorExecutionContext; foreach ($context as $k=>&$v) if (property_exists($context, $k)) { $bu_context->{$k}=$this->{"current_{$k}"}; $this->{"current_{$k}"}=&$v; } return $bu_context; } Given that we have a free in ZEND_ASSIGN_OBJ_SPEC_CV_CV_OP_DATA_VAR_HANDLER followed by a warning in ZEND_ASSIGN_REF_SPEC_VAR_CV_HANDLER, it is likely that $bu_context->{$k}=$this->{"current_{$k}"} causes the free of $this->{"current_{$k}"}, which $this->{"current_{$k}"}=&$v still tries to use (or at least destroy again). $this->{"current_{$k}"} at the time of the valgrind warning is [0x10877838] (refcount=0) reference: [0x11fa1f98] (refcount=7) object(EmulatorObject) #23128 ------------------------------------------------------------------------ The remainder of the comments for this report are too long. To view the rest of the comments, please view the bug report online at https://bugs.php.net/bug.php?id=72854 -- Edit this bug report at https://bugs.php.net/bug.php?id=72854&edit=1

« previous php.bugs (#203327) next »