summaryrefslogtreecommitdiff
path: root/ext/standard/tests/array
diff options
context:
space:
mode:
authorNikita Popov <nikic@php.net>2016-07-29 18:51:06 +0200
committerNikita Popov <nikic@php.net>2016-07-30 00:15:32 +0200
commit11e050920d3052d2b14a9cff6c4c5764d674fc46 (patch)
tree69bfd76ffdec6c214fb17845e0978d9a787d4203 /ext/standard/tests/array
parent261c436d8c5e93521edce531f650bae1fe814870 (diff)
downloadphp-git-11e050920d3052d2b14a9cff6c4c5764d674fc46.tar.gz
Fix memory unsafety in array_walk()
Fixes bugs #61967, #62607, #69068, #70713. The primary changes are: a) Use the ht_iterator mechanism to ensure safety not only if the iterated array itself changes, but also if it is replaced (and potentially destroyed) entirely. We use the same semantics for behavior under modification as foreach-by-reference. In particular, we advance to the next element before processing it. If the iterated entity is exchanged we iterate the new one from the start. If it is not an array/object we warn and abort. b) Always create a reference to the current value. Previously the code kept the value as a non-reference and updated it to the reference value produced by the user callback. However this is unsafe, as the array may have been reallocated in the meantime, so the previous value pointer is no longer value. c) Around a recursive walk, incref the reference containing the array. This ensures that the location where the currently iterated value is stored cannot be freed. One problem I was not able to solve is that we cannot decrement the apply count if the array is exchanged during a recursive walk.
Diffstat (limited to 'ext/standard/tests/array')
-rw-r--r--ext/standard/tests/array/bug61730.phpt7
-rw-r--r--ext/standard/tests/array/bug61967.phpt25
-rw-r--r--ext/standard/tests/array/bug62607.phpt12
-rw-r--r--ext/standard/tests/array/bug69068.phpt26
-rw-r--r--ext/standard/tests/array/bug69068_2.phpt34
-rw-r--r--ext/standard/tests/array/bug70713.phpt26
6 files changed, 126 insertions, 4 deletions
diff --git a/ext/standard/tests/array/bug61730.phpt b/ext/standard/tests/array/bug61730.phpt
index 0fe9f22212..0761fee774 100644
--- a/ext/standard/tests/array/bug61730.phpt
+++ b/ext/standard/tests/array/bug61730.phpt
@@ -28,10 +28,9 @@ array_walk(
print_r($myArray);
--EXPECT--
int(0)
-int(4)
-int(8)
+int(3)
+int(6)
+int(9)
Array
(
- [3] => 1
- [7] => 1
)
diff --git a/ext/standard/tests/array/bug61967.phpt b/ext/standard/tests/array/bug61967.phpt
new file mode 100644
index 0000000000..7fc65c8d90
--- /dev/null
+++ b/ext/standard/tests/array/bug61967.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Bug #61967: unset array item in array_walk_recursive cause inconsistent array
+--FILE--
+<?php
+$arr = array(
+ range(1, 5),
+ range(1, 5),
+ range(1, 5),
+ range(1, 5),
+ range(1, 5),
+);
+
+array_walk_recursive($arr,
+ function (&$value, $key) use(&$arr) {
+ var_dump($key);
+ unset($arr[$key]);
+ }
+);
+?>
+--EXPECT--
+int(0)
+int(1)
+int(2)
+int(3)
+int(4)
diff --git a/ext/standard/tests/array/bug62607.phpt b/ext/standard/tests/array/bug62607.phpt
new file mode 100644
index 0000000000..d9d529d2cf
--- /dev/null
+++ b/ext/standard/tests/array/bug62607.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Bug #62607: array_walk_recursive move internal pointer
+--FILE--
+<?php
+$arr = array('a'=>'b');
+echo 'Before -> '.current($arr).PHP_EOL;
+array_walk_recursive($arr, function(&$val){});
+echo 'After -> '.current($arr);
+?>
+--EXPECT--
+Before -> b
+After -> b
diff --git a/ext/standard/tests/array/bug69068.phpt b/ext/standard/tests/array/bug69068.phpt
new file mode 100644
index 0000000000..e87b759db0
--- /dev/null
+++ b/ext/standard/tests/array/bug69068.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Bug #69068: Exchanging array during array_walk -> memory errors
+--FILE--
+<?php
+
+$array = [1, 2, 3];
+array_walk($array, function($value, $key) {
+ var_dump($value);
+ if ($value == 2) {
+ $GLOBALS['array'] = [4, 5];
+ }
+});
+var_dump($array);
+
+?>
+--EXPECT--
+int(1)
+int(2)
+int(4)
+int(5)
+array(2) {
+ [0]=>
+ int(4)
+ [1]=>
+ int(5)
+}
diff --git a/ext/standard/tests/array/bug69068_2.phpt b/ext/standard/tests/array/bug69068_2.phpt
new file mode 100644
index 0000000000..ce3e3650a3
--- /dev/null
+++ b/ext/standard/tests/array/bug69068_2.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Bug #69068: Exchanging array during array_walk -> memory errors (variation)
+--FILE--
+<?php
+
+$array = [1, 2, 3];
+$array2 = [4, 5];
+array_walk($array, function(&$value, $key) use ($array2) {
+ var_dump($value);
+ if ($value == 2) {
+ $GLOBALS['array'] = $array2;
+ }
+ $value *= 10;
+});
+var_dump($array, $array2);
+
+?>
+--EXPECT--
+int(1)
+int(2)
+int(4)
+int(5)
+array(2) {
+ [0]=>
+ int(40)
+ [1]=>
+ int(50)
+}
+array(2) {
+ [0]=>
+ int(4)
+ [1]=>
+ int(5)
+}
diff --git a/ext/standard/tests/array/bug70713.phpt b/ext/standard/tests/array/bug70713.phpt
new file mode 100644
index 0000000000..4e2792ab4b
--- /dev/null
+++ b/ext/standard/tests/array/bug70713.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Bug #70713: Use After Free Vulnerability in array_walk()/array_walk_recursive()
+--FILE--
+<?php
+
+class obj
+{
+ function __tostring()
+ {
+ global $arr;
+
+ $arr = 1;
+ for ($i = 0; $i < 5; $i++) {
+ $v[$i] = 'hi'.$i;
+ }
+
+ return 'hi';
+ }
+}
+
+$arr = array('string' => new obj);
+array_walk_recursive($arr, 'settype');
+
+?>
+--EXPECTF--
+Warning: array_walk_recursive(): Iterated value is no longer an array or object in %s on line %d