Bug #67824 [Opn]: Combination of array_map, exceptions, anonymous functions causes corruption
| From: | requinix@php.net | Date: | Tue, 12 Aug 2014 01:30:40 +0000 |
| Subject: | Bug #67824 [Opn]: Combination of array_map, exceptions, anonymous functions causes corruption | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-187086@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=67824&edit=1
ID: 67824
Updated by: requinix@php.net
Reported by: tristan dot veness at gmail dot com
Summary: Combination of array_map, exceptions, anonymous
functions causes corruption
Status: Open
Type: Bug
Package: Scripting Engine problem
Operating System: Any
PHP Version: 5.5.15
Block user comment: N
Private report: N
New Comment:
See also bug #67807, which is very likely the same root cause exposed through a different mechanism.
Implying this is also potentially NAB.
Previous Comments:
------------------------------------------------------------------------
[2014-08-12 00:29:53] tristan dot veness at gmail dot com
Description:
------------
When using array_map with an anonymous function that throws an exception - and then manipulating a
copy of the stack trace, the original array passed to array_map becomes corrupted.
This is a fairly obscure edge case, but I managed to run into it within the framework I'm
currently working with.
Apologies for the long code sample, I have had alot of difficulty isolating the issue.
Test script:
---------------
<?php
$some_objects = [
new SomeClass(1), new SomeClass(2),
new SomeClass(3), new SomeClass(4),
];
$func = function(SomeClass $x) {
$x->some_function();
return $x;
};
array_walk($some_objects, $func);
var_dump($some_objects);
$mangled_result = array_map($func, $some_objects);
var_dump($mangled_result);
class SomeClass {
private $number;
public function __construct($number) {
$this->number = $number;
}
public function some_function() {
try {
throw new Exception();
}
catch (Exception $e) {
MANGLE_SOMEHOW($e->getTrace());
}
}
}
function MANGLE_SOMEHOW($trace) {
foreach ($trace as $i => $frame) {
if (isset($frame['args'])) {
foreach ($frame['args'] as $arg_index => $arg_value) {
// And here I was thinking this was a 'copy'???
$frame['args'][$arg_index] = $arg_value;
}
}
}
}
?>
Expected result:
----------------
array(4) {
[0]=>
object(SomeClass)#1 (1) {
["number":"SomeClass":private]=>
int(1)
}
[1]=>
object(SomeClass)#2 (1) {
["number":"SomeClass":private]=>
int(2)
}
[2]=>
object(SomeClass)#3 (1) {
["number":"SomeClass":private]=>
int(3)
}
[3]=>
object(SomeClass)#4 (1) {
["number":"SomeClass":private]=>
int(4)
}
}
array(4) {
[0]=>
object(SomeClass)#1 (1) {
["number":"SomeClass":private]=>
int(1)
}
[1]=>
object(SomeClass)#2 (1) {
["number":"SomeClass":private]=>
int(2)
}
[2]=>
object(SomeClass)#3 (1) {
["number":"SomeClass":private]=>
int(3)
}
[3]=>
object(SomeClass)#4 (1) {
["number":"SomeClass":private]=>
int(4)
}
}
Actual result:
--------------
array(4) {
[0]=>
object(SomeClass)#1 (1) {
["number":"SomeClass":private]=>
int(1)
}
[1]=>
object(SomeClass)#2 (1) {
["number":"SomeClass":private]=>
int(2)
}
[2]=>
object(SomeClass)#3 (1) {
["number":"SomeClass":private]=>
int(3)
}
[3]=>
object(SomeClass)#4 (1) {
["number":"SomeClass":private]=>
int(4)
}
}
array(3) {
[0]=>
object(SomeClass)#1 (1) {
["number":"SomeClass":private]=>
int(1)
}
[140627626761080]=>
object(SomeClass)#2 (1) {
["number":"SomeClass":private]=>
int(2)
}
[140627626759920]=>
object(SomeClass)#2 (1) {
["number":"SomeClass":private]=>
int(2)
}
}
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=67824&edit=1