Bug #64291 [Com]: Indeterminate GC of evaled lambda function resources

From: Date: Mon, 17 Mar 2014 18:58:20 +0000
Subject: Bug #64291 [Com]: Indeterminate GC of evaled lambda function resources
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-184815@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=64291&edit=1 ID: 64291 Comment by: ulrich dot eckhardt at base-42 dot de Reported by: Terry at ellisons dot org dot uk Summary: Indeterminate GC of evaled lambda function resources Status: Open Type: Bug Package: Scripting Engine problem Operating System: Ubuntu 12.10 PHP Version: 5.4.12 Block user comment: N Private report: N New Comment: I'm getting bitten by this bug, too. My use case is basically that I'm generating and eval()'ing code. Since this is not in a one-shot HTTP server process environment but in a longer running process that caches the generated code for performance reasons, this causes various failures that depend on the order in which requests are made. Using Terry's patch on PHP 5.4.24 on Debian/AMD64 caused the problem to go away, my services now run stable. What's keeping you from applying this patch (apart from the fact that it mixes tabs/spaces)? Previous Comments: ------------------------------------------------------------------------ [2013-12-05 16:15:36] Terry at ellisons dot org dot uk Dmitry, I have just realised that this "mangled names should be unique" issue applied to any runtime bound function or class as the following -- albeit perverse example shows: --TEST-- ISSUE #65915A Temporary class entries are not unique --INI-- opcache.enable=0 --SKIPIF-- --FILE-- <?php $tmp = tempnam(__DIR__, 'test'); foreach (['a','b'] as $f) { file_put_contents($tmp, <<<END <?php function $f() { class Hello { const WORLD = "Hello world from $f\\n"; } } END ); require $tmp; } a(); echo Hello::WORLD; unlink($tmp); ?> --CLEAN-- --EXPECT-- Hello world from a Here the two functions both compile a class with a mangled name "\0$class$filename$string_addr" which is the same for the a() and b() copies so b() version overwrites the a() one, and the DECLARE_CLASS opcode in a() incorrecly binds to the wrong class, hence Hello::WORLD incorrectly prints out the "from b" version. However, I suspect in practice that this is unlikely to manifest itself in real word apps. ------------------------------------------------------------------------ [2013-11-25 13:26:28] Terry at ellisons dot org dot uk Sorry, the above should read opcache.enable=0 though the failure is the same for opcache enabled and not enabled in the eval case. ------------------------------------------------------------------------ [2013-11-25 13:24:08] Terry at ellisons dot org dot uk And here's the eval version: --TEST-- ISSUE #64291 Temporary function entries for closures are not unique --INI-- opcache.enable=1 --FILE-- <?php foreach (['a','b'] as $f) { $tmp = "function $f() {return function(){ return '$f'; };}\n"; eval($tmp); echo $tmp; } $a = a(); $b = b(); printf( "%s, %s\n ", $a(), $b()); ?> --EXPECT-- function a() {return function(){ return 'a'; };} function b() {return function(){ return 'b'; };} a, b ------------------------------------------------------------------------ [2013-11-25 12:42:16] Terry at ellisons dot org dot uk What threw me was the botch with the temporary entries "\0{closure}$filenane$offset" are used in the EG(function_table). This is as clear as mud. When a file is compiled, a function table entry is created for each closue in the source. This entry is never executed directly, but is used by the ZEND_DECLARE_LAMBDA_FUNCTION to construct the closure object which contains a deep copy of this zend_function record. It is this copy that used when the closure is called. So long as the ZEND_DECLARE_LAMBDA_FUNCTION are executed within the same scope as the compile, this should normally be unique, but it is quite easy to construct a test case which the unique assumption fails: --TEST-- ISSUE #65915 Temporary function entries for closures are not unique --INI-- opcache.enable=0 --SKIPIF-- --FILE-- <?php $tmp = tempnam(__DIR__, 'test'); foreach (['a','b'] as $f) { file_put_contents($tmp, "<?php function $f() {return function(){ return '$f'; };}"); echo file_get_contents($tmp), "\n"; require $tmp; } $a = a(); $b = b(); printf( "%s, %s\n ", $a(), $b()); unlink($tmp); ?> --CLEAN-- --EXPECT-- <?php function a() {return function(){ return 'a'; };} <?php function b() {return function(){ return 'b'; };} a, b ------------------------------------------------------------------------ [2013-11-25 08:53:35] dmitry@php.net Your second script prints "a, a" only with OPCache, because it caches the included temporary file. Without OPCache it prints the expected "a, b". I also don't think that the first script indicates a bug. The more functions you create the more memory it requires. ------------------------------------------------------------------------ The remainder of the comments for this report are too long. To view the rest of the comments, please view the bug report online at https://bugs.php.net/bug.php?id=64291 -- Edit this bug report at https://bugs.php.net/bug.php?id=64291&edit=1

« previous php.bugs (#184815) next »