Bug #78032 [NEW]: Circular references from unset objects keep references to array elements
| From: | tutano2004 at gmail dot com | Date: | Sat, 18 May 2019 20:25:30 +0000 |
| Subject: | Bug #78032 [NEW]: Circular references from unset objects keep references to array elements | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-220892@lists.php.net to get a copy of this message | ||
From: tutano2004 at gmail dot com
Operating system:
PHP version: Irrelevant
Package: Scripting Engine problem
Bug Type: Bug
Bug description:Circular references from unset objects keep references to array elements
Description:
------------
When an array is transversed with the value passed by reference, in a
"foreach" loop, with the value then being passed by reference to another
function (in this case "doReferenceStuff"), if the function internally
instantiates 2 objects, each one with a reference to the other (circular
reference), and with at least 1 of them with a reference to the value
passed by reference, despite the fact that these objects are implicitly
unset at the end of function (and thus "seize to exist"), the array
still becomes an array of references in the end (by the time "var_dump"
is called) rather than an array of values.
As noted in the test script, calling gc_collect_cycles prevents this bug
from happening, meaning that something is not cleaned up well in these
cases before the garbage collection kicks in.
If the circular reference is not made between both objects, this doesn't
happen at all either.
The consequence of this later on is, in cases like this:
$array2 = $array;
$array2[1] = 'bar';
both $array and $array2 would have the element with the index 1 set as
'bar', rather than only the element in $array2 being set as such, given
that this element would be a reference rather than a value, incorrectly
so.
From my testing, this happens in every PHP version all the way back to
PHP 5.0 at the very least, and all the way forward up to the latest
stable version PHP 7.3.5.
I spent about 30min checking if there were any other bug reports of this
kind given the number of PHP versions affected, and I found none thus
far, hence creating this report, but finding the right words to
summarize this problem is hard, so there's a good chance this was
already reported but described differently.
None of the suggestions upon the submission of this report seem to point
out to this problem having been reported either.
On another note, what led me to find this bug is a use case that I have
in my own code, namely my own custom Exception class in a small
library/framework I have been developing over the years, extending from
the PHP Exception class, in which I instantiate with properties rather
than a message, and internally I have a properties manager class/object
that has a back reference to the Exception itself, so when an Exception
is thrown, it also has a reference to this manager, and the manager has
a reference to the Exception (creating a circular reference), and
whenever an Exception is thrown, the PHP engine itself stores a stack
trace within the Exception, and it's this stack trace which ends up
holding the reference (&$v), since in the stack one of the functions
receives the value by reference.
Test script:
---------------
function doReferenceStuff(&$v)
{
$c1 = new stdClass;
$c2 = new stdClass;
$c1->c2 = $c2;
$c2->c1 = $c1;
$c1->v = &$v;
}
$array = [1, 'foo', new stdClass];
foreach ($array as &$v) {
doReferenceStuff($v);
}
unset($v);
//gc_collect_cycles(); --> adding this here prevents the bug
var_dump($array);
Expected result:
----------------
array(3) {
[0]=>
int(1)
[1]=>
string(3) "foo"
[2]=>
object(stdClass)#7 (0) {
}
}
Actual result:
--------------
array(3) {
[0]=>
&int(1)
[1]=>
&string(3) "foo"
[2]=>
&object(stdClass)#7 (0) {
}
}
--
Edit bug report at https://bugs.php.net/bug.php?id=78032&edit=1
--
Try a snapshot (PHP 5.4): https://bugs.php.net/fix.php?id=78032&r=trysnapshot54
Try a snapshot (PHP 5.5): https://bugs.php.net/fix.php?id=78032&r=trysnapshot55
Try a snapshot (trunk): https://bugs.php.net/fix.php?id=78032&r=trysnapshottrunk
Fixed in SVN: https://bugs.php.net/fix.php?id=78032&r=fixed
Fixed in release: https://bugs.php.net/fix.php?id=78032&r=alreadyfixed
Need backtrace: https://bugs.php.net/fix.php?id=78032&r=needtrace
Need Reproduce Script: https://bugs.php.net/fix.php?id=78032&r=needscript
Try newer version: https://bugs.php.net/fix.php?id=78032&r=oldversion
Not developer issue: https://bugs.php.net/fix.php?id=78032&r=support
Expected behavior: https://bugs.php.net/fix.php?id=78032&r=notwrong
Not enough info: https://bugs.php.net/fix.php?id=78032&r=notenoughinfo
Submitted twice: https://bugs.php.net/fix.php?id=78032&r=submittedtwice
register_globals: https://bugs.php.net/fix.php?id=78032&r=globals
PHP 4 support discontinued: https://bugs.php.net/fix.php?id=78032&r=php4
Daylight Savings: https://bugs.php.net/fix.php?id=78032&r=dst
IIS Stability: https://bugs.php.net/fix.php?id=78032&r=isapi
Install GNU Sed: https://bugs.php.net/fix.php?id=78032&r=gnused
Floating point limitations: https://bugs.php.net/fix.php?id=78032&r=float
No Zend Extensions: https://bugs.php.net/fix.php?id=78032&r=nozend
MySQL Configuration Error: https://bugs.php.net/fix.php?id=78032&r=mysqlcfg