php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #44182 extract($a, EXTR_REFS) can fail to split copy-on-write references
Submitted: 2008-02-20 09:26 UTC Modified: 2008-11-26 01:03 UTC
Votes:1
Avg. Score:3.0 ± 0.0
Reproduced:0 of 0 (0.0%)
From: robin_fernandes at uk dot ibm dot com Assigned:
Status: Closed Package: Arrays related
PHP Version: 5CVS, 6CVS (2008-10-27) OS: *
Private report: No CVE-ID:
 [2008-02-20 09:26 UTC] robin_fernandes at uk dot ibm dot com
Description:
------------
extract($a, EXTR_REFS) does not always respect copy-on-write reference logic.

In the testcase below, $nonRef is initially a copy-on-write reference to an element of $a. After the call to extract, it has become a real reference to an element of $a.

This is reproducible on 5.2, 5.3 and 6.0 snaps.

Reproduce code:
---------------
<?php
$a = array('foo' => 'original.foo');

$nonref = $a['foo'];
$ref = &$a;

extract($a, EXTR_REFS);
$a['foo'] = 'changed.foo';

var_dump($nonref); //expecting original.foo
?>


Expected result:
----------------
string(12) "original.foo"

Actual result:
--------------
string(11) "changed.foo"

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2008-02-20 09:29 UTC] robin_fernandes at uk dot ibm dot com
I think the problem is that extract() can set the is_ref flag on zvals without splitting them (i.e. zvals which have more than 1 copy-on-write references can get their is_ref flag set).

From array.c:
...
} else {
  if (Z_REFCOUNT_P(var_array) > 1 || *entry == EG(uninitialized_zval_ptr)) {
    SEPARATE_ZVAL_TO_MAKE_IS_REF(entry);
  } else {
    Z_SET_ISREF_PP(entry);  // <-- causes damage if entry has COW references!
  }
...

I believe that the only reliable way to fix this would be to make extract() take its array argument as PREFER_REF.

This way, we can safely split array elements away from their copy-on-write references, and still end up with a zval that belongs to the original array.

I'm attaching a patch which implements this and fixes this bug, as well as bug http://bugs.php.net/44181.

Disclaimer:
- I am not an internals expert. I might have disregarded some scenarios. Feedback welcome!
- Patch tested on Windows XP 32bit only.

Patch against php6.0-200802191530 snap (includes test cases): http://pastebin.ca/910172
 [2008-10-25 13:25 UTC] jani@php.net
That url does not work..
 [2008-10-25 13:52 UTC] robin_fernandes at uk dot ibm dot com
The patch URL? Seems OK to me, perhaps it was temporarily broken.

In any case, here's another one: 
http://soal.org/php/extract.patch.txt
 [2008-10-27 13:01 UTC] jani@php.net
Does this bug exist in HEAD, PHP_5_3 and PHP_5_2 branches? And in today's versions of them?
 [2008-10-27 13:20 UTC] robin_fernandes at uk dot ibm dot com
Confirmed this bug (and bug 44181) still present on:
PHP 5.2.7RC3-dev (cli) (built: Oct 27 2008 11:40:07) 
PHP 5.3.0alpha3-dev (cli) (built: Oct 27 2008 14:06:30) 
PHP 6.0.0-dev (cli) (built: Oct 27 2008 12:39:36)
 [2008-11-26 01:03 UTC] lbarnaud@php.net
This bug has been fixed in CVS.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.

Thanks for the patch
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Wed Apr 16 13:02:46 2014 UTC