magic property guards are not reset in timeout
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 40.4k
- Forks
- 8.1k
- Avg merge
- 2d 13h
- Merged PRs (30d)
- 96
Description
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->fooinside of __get() will returnnulland raise anE_WARNINGif there is nofooproperty defined, rather than calling __get() a second time. However, overload methods may invoke other overload methods implicitly (such as __set() triggering __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
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:
- 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
- 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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the property-guard logic in Zend/zend_object_handlers.c at the referenced lines, then trace zend_interrupt_helper and ZEND_VM_INTERRUPT for timeout handling. Use the supplied PHP reproduction to observe the shutdown behavior; done means a timeout inside __get() does not leave the guard active and the shutdown handler produces the expected property lookup result without the undefined-property warning.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100