php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #54039 use() of static variables in lambda functions can break staticness
Submitted: 2011-02-18 00:49 UTC Modified: 2011-04-08 12:03 UTC
From: a at b dot c dot de Assigned: dmitry
Status: Closed Package: Scripting Engine problem
PHP Version: 5.3.5 OS: Windows XP
Private report: No CVE-ID:
 [2011-02-18 00:49 UTC] a at b dot c dot de
Description:
------------
Let an ordinary function declare a static local variable, and let it also define an anonymous function that uses that static variable. It can happen that the static variable no longer retains its value across calls to the ordinary function; i.e., it loses that static property.

It seems to be limited to situations where the anonymous function is defined before the static variable has had its value changed.

Test script:
---------------
function test_1()
{
	static $v = 0;
	++$v;
	echo "Outer function increments \$v to $v\n";
	$f = function()use($v)
	{
		echo "Inner function reckons \$v is $v\n";
	};
	return $f;
}

echo "\nIncrement static variable, then use it in anonymous function definition:\n";
$f = test_1(); $f();
$f = test_1(); $f();

function test_2()
{
	static $v = 0;
	$f = function()use($v)
	{
		echo "Inner function reckons \$v is $v\n";
	};
	++$v;
	echo "Outer function increments \$v to $v\n";
	return $f;
}

echo "\nUse static variable in anonymous function definition, then increment it:\n";
$f = test_2(); $f();
$f = test_2(); $f();


Expected result:
----------------
Increment static variable, then use it in anonymous function definition:
Outer function increments $v to 1
Inner function reckons $v is 1
Outer function increments $v to 2
Inner function reckons $v is 2

Use static variable in anonymous function definition, then increment it:
Outer function increments $v to 1
Inner function reckons $v is 0
Outer function increments $v to 2
Inner function reckons $v is 1

Actual result:
--------------
Increment static variable, then use it in anonymous function definition:
Outer function increments $v to 1
Inner function reckons $v is 1
Outer function increments $v to 2
Inner function reckons $v is 2

Use static variable in anonymous function definition, then increment it:
Outer function increments $v to 1
Inner function reckons $v is 0
Outer function increments $v to 1
Inner function reckons $v is 0

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-02-18 04:16 UTC] a at b dot c dot de
A little further testing gives this:

function test_1()
{
	static $v = '';
	$v .= 'b';
	echo "Outer function catenates 'b' onto \$v to give $v\n";
	$f = function()use($v)
	{
		echo "Inner function reckons \$v is $v\n";
	};
	$v .= 'a';
	echo "Outer function catenates 'a' onto \$v to give $v\n";
	return $f;
}
$f = test_1(); $f();
$f = test_1(); $f();
$f = test_1(); $f();
$f = test_1(); $f();

The second concatenation ('a') fails to be retained; if only the anonymous function didn't use($v), the variable would end up containing 'babababa'.
 [2011-02-18 13:54 UTC] cataphract@php.net
-Status: Open +Status: Feedback
 [2011-02-18 13:54 UTC] cataphract@php.net
Please try using this snapshot:

  http://snaps.php.net/php5.3-latest.tar.gz
 
For Windows:

  http://windows.php.net/snapshots/

Bug #53958 is similar and is fixed in SVN.
 [2011-02-18 13:59 UTC] cataphract@php.net
For windows, the address will be http://rmtools.php.net/snaps/5.3-ts-windows-vc9-x86/builds/php-5.3-ts-windows-vc9-x86-r308443.zip (or similar for other compiler/thread safety combination)
 [2011-02-19 03:29 UTC] a at b dot c dot de
-Status: Feedback +Status: Open
 [2011-02-19 03:29 UTC] a at b dot c dot de
The cited snapshot (the VC6 build, FYI) shows the correct behaviour for the incrementing tests.

$v remains static in the string concatenation test as well, but there is what is probably an additional issue at play there:

Outer function catenates 'b' onto $v to give b
Outer function catenates 'a' onto $v to give ba
Inner function reckons $v is b
Outer function catenates 'b' onto $v to give bab
Outer function catenates 'a' onto $v to give baba
Inner function reckons $v is X☼←
Outer function catenates 'b' onto $v to give X☼←☺b
Outer function catenates 'a' onto $v to give X☼←☺ba
Inner function reckons $v is  ↕←☺b
Outer function catenates 'b' onto $v to give  ↕←☺bab
Outer function catenates 'a' onto $v to give  ↕←☺baba
Inner function reckons $v is  ↕←☺bab
 [2011-02-21 08:34 UTC] cataphract@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: dmitry
 [2011-02-21 08:34 UTC] cataphract@php.net
The concatenation example indeed shows memory corruption, with invalid free's and reads of freed data,
 [2011-04-08 12:02 UTC] dmitry@php.net
Automatic comment from SVN on behalf of dmitry
Revision: http://svn.php.net/viewvc/?view=revision&revision=310050
Log: - Fixed bug #54358 (Closure, use and reference)
- Fixed bug #54039 (use() of static variables in lambda functions can break staticness)
 [2011-04-08 12:03 UTC] dmitry@php.net
-Status: Assigned +Status: Closed
 [2011-04-08 12:03 UTC] dmitry@php.net
This bug has been fixed in SVN.

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.


 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Wed Apr 23 07:02:14 2014 UTC