[php-src] Issue #14983: magic property guards are not reset in timeout

From: Date: Tue, 16 Jul 2024 18:39:48 +0000
Subject: [php-src] Issue #14983: magic property guards are not reset in timeout
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-247305@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/14983 Author: ivantsepp ### Description The overloaded methods are protected via property guards flags from circular calls. An example is at https://github.com/php/php-src/blob/0f398a437e401fc1abf2a23196918ba1ccb5d8c5/Zend/zend_object_handlers.c#L724-L726 However, these guards are not reset when a timeout happens within the overloaded method. We will get undefined property warnings in the shutdown handlers as a result. This is because PHP still thinks we are inside the __get method From https://www.php.net/manual/en/language.oop5.overloading.php#object.get: > PHP will not call an overloaded method from within the same overloaded method. That means, for > example, writing return $this->foo inside > of [__get()](https://www.php.net/manual/en/language.oop5.overloading.php#object.get) will > return null and raise > an **[E_WARNING](https://www.php.net/manual/en/errorfunc.constants.php#constant.e-warning)** if > there is no foo property defined, rather than > calling [__get()](https://www.php.net/manual/en/language.oop5.overloading.php#object.get) a second > time. However, overload methods may invoke other overload methods implicitly (such > as [__set()](https://www.php.net/manual/en/language.oop5.overloading.php#object.set) triggering [__get()](https://www.php.net/manual/en/language.oop5.overloading.php#object.get)). However, I believe that we are no longer inside the \_\_get call when we are in the shutdown handlers and we should allow further \_\_get calls to work. Here's a repro script (https://3v4l.org/DhXZH) : ```php <?php ini_set('max_execution_time', 1); set_error_handler(null, E_ALL); class A { function __get($name) { return $name; } } global $a; $a = new A; function shutdown() { global $a; // Warning: Undefined property: A::$foo ... var_dump($a->foo); } register_shutdown_function('shutdown'); // Try to trigger "Fatal error: Maximum execution time of 1 second exceeded" // within the A::__get() method. for ($i = 0; $i < 100000000; $i++) { $a->foo; } ``` And the possible outputs are: 1. The timeout is triggered outside of the __get call (A ``` Fatal error: Maximum execution time of 1 second exceeded in /home/itse/development/Etsyweb/ivantestcase.php on line 26 string(3) "foo" ``` or 2. The timeout is triggered within the __get call: ``` Fatal error: Maximum execution time of 1 second exceeded in /home/itse/development/Etsyweb/ivantestcase.php on line 9 Warning: Undefined property: A::$foo in /home/itse/development/Etsyweb/ivantestcase.php on line 18 NULL ``` I expect output 1 no matter when the timeout occurs. Output 2 shouldn't occur. I attempted to dig into the source code for a fix. Perhaps in the zend_interrupt_helper that gets called from ZEND_VM_INTERRUPT, we can look at the call frames and reset the property guards? I'm not too familiar with PHP Virtual Machine so I am not sure if this is the correct approach. https://github.com/php/php-src/compare/f58a3c392f4fc0ce2f82935399c5e6e64441e2e9...ivantsepp:php-src:61e0b6293829a508884de388f99ab093be9e4563?expand=1 Let me know if this is a bug that should be addressed and I would appreciate any pointers on what a potential fix might look like! I am interested in working on a fix if so. ### PHP Version PHP 8.4.0-dev ### Operating System macOS 13.6

« previous php.bugs (#247305) next »