Bug #69038 [Com]: switch(SOMECONSTANT) misbehaves
| From: | php at bof dot de | Date: | Thu, 12 Feb 2015 09:57:12 +0000 |
| Subject: | Bug #69038 [Com]: switch(SOMECONSTANT) misbehaves | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-190615@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=69038&edit=1
ID: 69038
Comment by: php at bof dot de
Reported by: php at bof dot de
Summary: switch(SOMECONSTANT) misbehaves
Status: Open
Type: Bug
Package: opcache
Operating System: Linux
PHP Version: 5.6.5
Block user comment: N
Private report: N
New Comment:
Playing around with the ordering of the cases:
- leaving out default: altogether does not remove the issue
- ordering the problematic case 'foo': behind the default does not remove the issue
- putting the default: as the first thing does not remove the issue AND changes valgrind output a
bit (two instead of three blocks of backtraces)
This gives me a more minimal reproducer:
define('OK', 'ok');
function badswitch() {
switch(OK) {
default: return 'bad';
case 'ok': return 'ok';
}
}
var_dump(badswitch());
with valgrind output
Thu Feb 12 10:55:44 2015 (6761): Debug Loading blacklist file:
'/opt/php/ini.d/opcache.blacklist'
Thu Feb 12 10:55:44 2015 (6761): Message Cached script
'/home/patrick/webdev/switch_const_myconst.php'
==6761== Conditional jump or move depends on uninitialised value(s)
==6761== at 0xA8A33C: compare_function (zend_operators.c:1602)
==6761== by 0xA8BE21: is_equal_function (zend_operators.c:1837)
==6761== by 0xAEA4E1: ZEND_CASE_SPEC_TMP_CONST_HANDLER (zend_vm_execute.h:9364)
==6761== by 0xAD67D9: execute_ex (zend_vm_execute.h:363)
==6761== by 0xAD685E: zend_execute (zend_vm_execute.h:388)
==6761== by 0xA91EB9: zend_execute_scripts (zend.c:1341)
==6761== by 0x9FBC55: php_execute_script (main.c:2584)
==6761== by 0xB4618E: do_cli (php_cli.c:994)
==6761== by 0xB47239: main (php_cli.c:1378)
==6761==
==6761== Use of uninitialised value of size 8
==6761== at 0xA8A353: compare_function (zend_operators.c:1602)
==6761== by 0xA8BE21: is_equal_function (zend_operators.c:1837)
==6761== by 0xAEA4E1: ZEND_CASE_SPEC_TMP_CONST_HANDLER (zend_vm_execute.h:9364)
==6761== by 0xAD67D9: execute_ex (zend_vm_execute.h:363)
==6761== by 0xAD685E: zend_execute (zend_vm_execute.h:388)
==6761== by 0xA91EB9: zend_execute_scripts (zend.c:1341)
==6761== by 0x9FBC55: php_execute_script (main.c:2584)
==6761== by 0xB4618E: do_cli (php_cli.c:994)
==6761== by 0xB47239: main (php_cli.c:1378)
==6761==
string(3) "bad"
Previous Comments:
------------------------------------------------------------------------
[2015-02-12 09:46:43] php at bof dot de
The issue remains when I change the case "label" from a constant string to a variable; the
valgrind output is slightly different then:
Code:
define('MYCONST', 'foo');
function goodswitch() {
$foo = 'foo';
switch(MYCONST) {
case $foo: return 'foo';
case 'bar': return 'bar';
default: return 'default';
}
}
function badswitch() {
$foo = 'foo';
switch(MYCONST) {
case 'bar': return 'bar';
case $foo: return 'foo';
default: return 'default';
}
}
var_dump(goodswitch());
var_dump(badswitch());
Valgrind:
Thu Feb 12 10:44:21 2015 (4062): Debug Loading blacklist file:
'/opt/php/ini.d/opcache.blacklist'
Thu Feb 12 10:44:21 2015 (4062): Message Cached script
'/home/patrick/webdev/switch_const_myconst.php'
string(3) "foo"
==4062== Conditional jump or move depends on uninitialised value(s)
==4062== at 0xA8A33C: compare_function (zend_operators.c:1602)
==4062== by 0xA8BE21: is_equal_function (zend_operators.c:1837)
==4062== by 0xAF1508: ZEND_CASE_SPEC_TMP_CV_HANDLER (zend_vm_execute.h:12541)
==4062== by 0xAD67D9: execute_ex (zend_vm_execute.h:363)
==4062== by 0xAD685E: zend_execute (zend_vm_execute.h:388)
==4062== by 0xA91EB9: zend_execute_scripts (zend.c:1341)
==4062== by 0x9FBC55: php_execute_script (main.c:2584)
==4062== by 0xB4618E: do_cli (php_cli.c:994)
==4062== by 0xB47239: main (php_cli.c:1378)
==4062==
==4062== Use of uninitialised value of size 8
==4062== at 0xA8A353: compare_function (zend_operators.c:1602)
==4062== by 0xA8BE21: is_equal_function (zend_operators.c:1837)
==4062== by 0xAF1508: ZEND_CASE_SPEC_TMP_CV_HANDLER (zend_vm_execute.h:12541)
==4062== by 0xAD67D9: execute_ex (zend_vm_execute.h:363)
==4062== by 0xAD685E: zend_execute (zend_vm_execute.h:388)
==4062== by 0xA91EB9: zend_execute_scripts (zend.c:1341)
==4062== by 0x9FBC55: php_execute_script (main.c:2584)
==4062== by 0xB4618E: do_cli (php_cli.c:994)
==4062== by 0xB47239: main (php_cli.c:1378)
==4062==
==4062== Conditional jump or move depends on uninitialised value(s)
==4062== at 0xAD0D9C: _zval_dtor (zend_variables.h:32)
==4062== by 0xAE6F4E: ZEND_FREE_SPEC_TMP_HANDLER (zend_vm_execute.h:7956)
==4062== by 0xAD67D9: execute_ex (zend_vm_execute.h:363)
==4062== by 0xAD685E: zend_execute (zend_vm_execute.h:388)
==4062== by 0xA91EB9: zend_execute_scripts (zend.c:1341)
==4062== by 0x9FBC55: php_execute_script (main.c:2584)
==4062== by 0xB4618E: do_cli (php_cli.c:994)
==4062== by 0xB47239: main (php_cli.c:1378)
==4062==
string(7) "default"
------------------------------------------------------------------------
[2015-02-12 09:29:48] php at bof dot de
BTW, same valgrind result (a bit shorter backtrace) after disabling xdebug and any extension other
than opcache.
Disabling opcache also, removes the issue, shows foo+foo and no valgrind noise.
------------------------------------------------------------------------
[2015-02-12 09:23:35] php at bof dot de
valgrind has something to say on the issue!
patrick@dev0:{dbg-5.6.5}> valgrind php -d opcache.enable_cli=1 ~/webdev/switch_const_myconst.php
==30622== Memcheck, a memory error detector
==30622== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==30622== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==30622== Command: php -d opcache.enable_cli=1 /home/patrick/webdev/switch_const_myconst.php
==30622==
Thu Feb 12 10:22:35 2015 (30622): Debug Loading blacklist file:
'/opt/php/ini.d/opcache.blacklist'
Thu Feb 12 10:22:35 2015 (30622): Message Cached script
'/home/patrick/webdev/switch_const_myconst.php'
string(3) "foo"
==30622== Conditional jump or move depends on uninitialised value(s)
==30622== at 0xA8A33C: compare_function (zend_operators.c:1602)
==30622== by 0xA8BE21: is_equal_function (zend_operators.c:1837)
==30622== by 0xAEA4E1: ZEND_CASE_SPEC_TMP_CONST_HANDLER (zend_vm_execute.h:9364)
==30622== by 0xAD67D9: execute_ex (zend_vm_execute.h:363)
==30622== by 0xCC31BE3: xdebug_execute_ex (xdebug.c:1437)
==30622== by 0xAD685E: zend_execute (zend_vm_execute.h:388)
==30622== by 0xAD7390: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:592)
==30622== by 0xADCB02: ZEND_DO_FCALL_SPEC_CONST_HANDLER (zend_vm_execute.h:2595)
==30622== by 0xAD67D9: execute_ex (zend_vm_execute.h:363)
==30622== by 0xCC31BE3: xdebug_execute_ex (xdebug.c:1437)
==30622== by 0xAD685E: zend_execute (zend_vm_execute.h:388)
==30622== by 0xA91EB9: zend_execute_scripts (zend.c:1341)
==30622==
==30622== Use of uninitialised value of size 8
==30622== at 0xA8A353: compare_function (zend_operators.c:1602)
==30622== by 0xA8BE21: is_equal_function (zend_operators.c:1837)
==30622== by 0xAEA4E1: ZEND_CASE_SPEC_TMP_CONST_HANDLER (zend_vm_execute.h:9364)
==30622== by 0xAD67D9: execute_ex (zend_vm_execute.h:363)
==30622== by 0xCC31BE3: xdebug_execute_ex (xdebug.c:1437)
==30622== by 0xAD685E: zend_execute (zend_vm_execute.h:388)
==30622== by 0xAD7390: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:592)
==30622== by 0xADCB02: ZEND_DO_FCALL_SPEC_CONST_HANDLER (zend_vm_execute.h:2595)
==30622== by 0xAD67D9: execute_ex (zend_vm_execute.h:363)
==30622== by 0xCC31BE3: xdebug_execute_ex (xdebug.c:1437)
==30622== by 0xAD685E: zend_execute (zend_vm_execute.h:388)
==30622== by 0xA91EB9: zend_execute_scripts (zend.c:1341)
==30622==
==30622== Conditional jump or move depends on uninitialised value(s)
==30622== at 0xAD0D9C: _zval_dtor (zend_variables.h:32)
==30622== by 0xAE6F4E: ZEND_FREE_SPEC_TMP_HANDLER (zend_vm_execute.h:7956)
==30622== by 0xAD67D9: execute_ex (zend_vm_execute.h:363)
==30622== by 0xCC31BE3: xdebug_execute_ex (xdebug.c:1437)
==30622== by 0xAD685E: zend_execute (zend_vm_execute.h:388)
==30622== by 0xAD7390: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:592)
==30622== by 0xADCB02: ZEND_DO_FCALL_SPEC_CONST_HANDLER (zend_vm_execute.h:2595)
==30622== by 0xAD67D9: execute_ex (zend_vm_execute.h:363)
==30622== by 0xCC31BE3: xdebug_execute_ex (xdebug.c:1437)
==30622== by 0xAD685E: zend_execute (zend_vm_execute.h:388)
==30622== by 0xA91EB9: zend_execute_scripts (zend.c:1341)
==30622== by 0x9FBC55: php_execute_script (main.c:2584)
==30622==
string(7) "default"
==30622==
==30622== HEAP SUMMARY:
==30622== in use at exit: 5,556 bytes in 45 blocks
==30622== total heap usage: 34,708 allocs, 34,663 frees, 5,475,889 bytes allocated
==30622==
==30622== LEAK SUMMARY:
==30622== definitely lost: 733 bytes in 26 blocks
==30622== indirectly lost: 0 bytes in 0 blocks
==30622== possibly lost: 0 bytes in 0 blocks
==30622== still reachable: 4,823 bytes in 19 blocks
==30622== suppressed: 0 bytes in 0 blocks
==30622== Rerun with --leak-check=full to see details of leaked memory
==30622==
==30622== For counts of detected and suppressed errors, rerun with: -v
==30622== Use --track-origins=yes to see where uninitialised values come from
==30622== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 6 from 6)
------------------------------------------------------------------------
[2015-02-12 09:20:15] php at bof dot de
I cannot get it to segfault / dump core on an --enable-debug build. Weird. Tried it a few hundred
times. However the issue is still there (outputs foo and default).
With the production build, identical in all other regards, I can get it to segfault in one of about
10 runs. Here is a backtrace of such a run, for whatever that's worth:
Core was generated by `php -d opcache.enable_cli=1
/home/patrick/webdev/switch_const_myconst.php'.
Program terminated with signal 11, Segmentation fault.
#0 0x0000000000000021 in ?? ()
(gdb) bt
#0 0x0000000000000021 in ?? ()
#1 0x0000000000853d3b in zend_hash_destroy (ht=0x7f941c441e18)
at /usr/src/phb/build/release-5.6.5/php-src/Zend/zend_hash.c:548
#2 0x0000000000844893 in _zval_dtor_func (zvalue=0x7f941c4401b0)
at /usr/src/phb/build/release-5.6.5/php-src/Zend/zend_variables.c:45
#3 0x000000000087368a in _zval_dtor (execute_data=0x7f941c4401d0)
at /usr/src/phb/build/release-5.6.5/php-src/Zend/zend_variables.h:35
#4 ZEND_FREE_SPEC_TMP_HANDLER (execute_data=0x7f941c4401d0)
at /usr/src/phb/build/release-5.6.5/php-src/Zend/zend_vm_execute.h:7956
#5 0x00000000008b07c9 in execute_ex (execute_data=0x7f941c4401d0)
at /usr/src/phb/build/release-5.6.5/php-src/Zend/zend_vm_execute.h:363
#6 0x00007f941467947b in xdebug_execute_ex (execute_data=0x7f941c4401d0)
at /usr/src/phb/build/release-5.6.5/xdebug/xdebug.c:1437
#7 0x00000000008e7ac2 in zend_do_fcall_common_helper_SPEC (
execute_data=<value optimized out>)
at /usr/src/phb/build/release-5.6.5/php-src/Zend/zend_vm_execute.h:592
#8 0x00000000008b07c9 in execute_ex (execute_data=0x7f941c4400e0)
at /usr/src/phb/build/release-5.6.5/php-src/Zend/zend_vm_execute.h:363
#9 0x00007f941467947b in xdebug_execute_ex (execute_data=0x7f941c4400e0)
at /usr/src/phb/build/release-5.6.5/xdebug/xdebug.c:1437
#10 0x00000000008472d4 in zend_execute_scripts (type=8, retval=0x0,
file_count=3) at /usr/src/phb/build/release-5.6.5/php-src/Zend/zend.c:1341
#11 0x00000000007e3bfe in php_execute_script (primary_file=0x7fffaabc56f0)
at /usr/src/phb/build/release-5.6.5/php-src/main/main.c:2584
#12 0x00000000008eaf89 in do_cli (argc=4, argv=0x1f00e90)
at /usr/src/phb/build/release-5.6.5/php-src/sapi/cli/php_cli.c:994
#13 0x00000000008eb91c in main (argc=4, argv=0x1f00e90)
at /usr/src/phb/build/release-5.6.5/php-src/sapi/cli/php_cli.c:1378
------------------------------------------------------------------------
[2015-02-12 08:43:18] php at bof dot de
Description:
------------
Using a self built PHP 5.6.5, opcache enabled:
switch(ARBITRARYCONSTANTS) misbehaves: goes to the default case, unless the matching thing is the
first case.
opcache is enabled like this:
zend_extension=/opt/php/extensions/opcache.so
opcache.enable_cli=1
Also happens under mod_php without opcache.enable_cli. No other opcache settings made, but happens
also with our usual production settings, so that should not matter
The result is usually what I show under "actual result", below, but on some runs I also
see a segmentation fault, or the correct behaviour.
I can reproduce the problem when using global "const MYCONST = 'foo';", but
apparently _not_ with a class constant (class foo { const MYCONST = 'foo'; }
Will create a debug build of PHP soon and try to capture a backtrace for a test run where it runs
into a segmentation violation. Might take some time, though...
Test script:
---------------
<?php
define('MYCONST', 'foo');
function goodswitch() {
switch(MYCONST) {
case 'foo': return 'foo';
case 'bar': return 'bar';
default: return 'default';
}
}
function badswitch() {
switch(MYCONST) {
case 'bar': return 'bar';
case 'foo': return 'foo';
default: return 'default';
}
}
var_dump(goodswitch());
var_dump(badswitch());
?>
Expected result:
----------------
string(3) "foo"
string(3) "foo"
Actual result:
--------------
string(3) "foo"
string(7) "default"
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=69038&edit=1