-
Notifications
You must be signed in to change notification settings - Fork 8k
Description
Description
In our project we've started adapting PHP enums and on our first "bigger scale" use case we're running into a segfault - seemingly on accessing an enum case.
gdb stacktrace:
Program received signal SIGSEGV, Segmentation fault.
zval_addref_p (pz=0x4412edb0) at ./Zend/zend_types.h:1240
1240 ./Zend/zend_types.h: No such file or directory.
(gdb) bt
#0 zval_addref_p (pz=0x4412edb0) at ./Zend/zend_types.h:1240
#1 zend_separate_class_constants_table (class_type=class_type@entry=0x4412e5a8) at ./Zend/zend_API.c:1331
#2 0x0000558eeb6306c8 in zend_class_constants_table (ce=0x4412e5a8) at ./Zend/zend_API.h:431
#3 zend_get_class_constant_ex (class_name=0x41758028, constant_name=0x41b8fd30, scope=scope@entry=0x4708c120, flags=512)
at ./Zend/zend_constants.c:364
#4 0x0000558eeb6ca50c in zend_ast_evaluate (result=result@entry=0x7fff0b412fd0, ast=ast@entry=0x4708c4c8, scope=0x4708c120)
at ./Zend/zend_ast.c:777
#5 0x0000558eeb6329ec in zval_update_constant_ex (scope=<optimized out>, p=0x7fb736c14440) at ./Zend/zend_execute_API.c:696
#6 zval_update_constant_ex (p=0x7fb736c14440, scope=<optimized out>) at ./Zend/zend_execute_API.c:671
#7 0x0000558eeb6ad06e in ZEND_RECV_INIT_SPEC_CONST_HANDLER () at ./Zend/zend_vm_execute.h:3736
#8 execute_ex (ex=0x4412e5a8) at ./Zend/zend_vm_execute.h:55951
#9 0x0000558eeb6b037d in zend_execute (op_array=0x7fb736c7b000, return_value=0x0) at ./Zend/zend_vm_execute.h:60163
#10 0x0000558eeb6421ed in zend_execute_scripts (type=type@entry=8, retval=retval@entry=0x0, file_count=file_count@entry=3)
at ./Zend/zend.c:1846
#11 0x0000558eeb5dda41 in php_execute_script (primary_file=primary_file@entry=0x7fff0b415680) at ./main/main.c:2542
#12 0x0000558eeb487e0e in main (argc=<optimized out>, argv=<optimized out>) at ./sapi/fpm/fpm/fpm_main.c:1935
(gdb) zbacktrace
[0x7fb736c143d0] [...]\BoostHintVO->create("happiness_amount", 0)
[...]/BoostHintVO.php:16
[0x7fb736c14280] [...]\BonusOnSetAdjacencyAbilityFactory->buildAdjacencyBonuses(
array(3)[0x7fb736c142d0]) /[...]/BonusOnSetAdjacencyAbilityFactory.php:75
[...]
(gdb) backtrace full
#0 zval_addref_p (pz=0x4412edb0) at ./Zend/zend_types.h:1240
No locals.
#1 zend_separate_class_constants_table (class_type=class_type@entry=0x4412e5a8) at ./Zend/zend_API.c:1331
_z = <optimized out>
__ht = 0x4412e658
_p = <optimized out>
_end = 0x4412ec80
mutable_data = <optimized out>
constants_table = 0x7fb7314c14b8
key = 0x41b8fd30
new_c = <optimized out>
c = 0x4412edb0
#2 0x0000558eeb6306c8 in zend_class_constants_table (ce=0x4412e5a8) at ./Zend/zend_API.h:431
mutable_data = <optimized out>
#3 zend_get_class_constant_ex (class_name=0x41758028, constant_name=0x41b8fd30, scope=scope@entry=0x4708c120, flags=512)
at ./Zend/zend_constants.c:364
ce = 0x4412e5a8
c = 0x0
ret_constant = 0x0
#4 0x0000558eeb6ca50c in zend_ast_evaluate (result=result@entry=0x7fff0b412fd0, ast=ast@entry=0x4708c4c8, scope=0x4708c120)
at ./Zend/zend_ast.c:777
class_name = <optimized out>
const_name = <optimized out>
zv = <optimized out>
op1 = {value = {lval = 1103188432, dval = 5.450475051406721e-315, counted = 0x41c151d0, str = 0x41c151d0, arr = 0x41c151d0,
obj = 0x41c151d0, res = 0x41c151d0, ref = 0x41c151d0, ast = 0x41c151d0, zv = 0x41c151d0, ptr = 0x41c151d0, ce = 0x41c151d0,
func = 0x41c151d0, ww = {w1 = 1103188432, w2 = 0}}, u1 = {type_info = 3949145561, v = {type = 217 '\331', type_flags = 45 '-',
u = {extra = 60259}}}, u2 = {next = 21902, cache_slot = 21902, opline_num = 21902, lineno = 21902, num_args = 21902,
fe_pos = 21902, fe_iter_idx = 21902, property_guard = 21902, constant_flags = 21902, extra = 21902}}
op2 = {value = {lval = 94072325571128, dval = 4.6477904289087367e-310, counted = 0x558eeb8c8238 <zend_autoload>,
str = 0x558eeb8c8238 <zend_autoload>, arr = 0x558eeb8c8238 <zend_autoload>, obj = 0x558eeb8c8238 <zend_autoload>,
res = 0x558eeb8c8238 <zend_autoload>, ref = 0x558eeb8c8238 <zend_autoload>, ast = 0x558eeb8c8238 <zend_autoload>,
zv = 0x558eeb8c8238 <zend_autoload>, ptr = 0x558eeb8c8238 <zend_autoload>, ce = 0x558eeb8c8238 <zend_autoload>,
func = 0x558eeb8c8238 <zend_autoload>, ww = {w1 = 3951854136, w2 = 21902}}, u1 = {type_info = 7, v = {type = 7 '\a',
type_flags = 0 '\000', u = {extra = 0}}}, u2 = {next = 21902, cache_slot = 21902, opline_num = 21902, lineno = 21902,
num_args = 21902, fe_pos = 21902, fe_iter_idx = 21902, property_guard = 21902, constant_flags = 21902, extra = 21902}}
ret = SUCCESS
#5 0x0000558eeb6329ec in zval_update_constant_ex (scope=<optimized out>, p=0x7fb736c14440) at ./Zend/zend_execute_API.c:696
tmp = {value = {lval = 776, dval = 3.8339494117280732e-321, counted = 0x308, str = 0x308, arr = 0x308, obj = 0x308, res = 0x308,
ref = 0x308, ast = 0x308, zv = 0x308, ptr = 0x308, ce = 0x308, func = 0x308, ww = {w1 = 776, w2 = 0}}, u1 = {
type_info = 2236897536, v = {type = 0 '\000', type_flags = 89 'Y', u = {extra = 34132}}}, u2 = {next = 1646290205,
cache_slot = 1646290205, opline_num = 1646290205, lineno = 1646290205, num_args = 1646290205, fe_pos = 1646290205,
fe_iter_idx = 1646290205, property_guard = 1646290205, constant_flags = 1646290205, extra = 1646290205}}
ast_ref = 0x4708c4c0
ast_is_refcounted = <optimized out>
result = <optimized out>
ast = 0x4708c4c8
#6 zval_update_constant_ex (p=0x7fb736c14440, scope=<optimized out>) at ./Zend/zend_execute_API.c:671
ast = <optimized out>
name = <optimized out>
zv = <optimized out>
_z1 = <optimized out>
_z2 = <optimized out>
_gc = <optimized out>
_t = <optimized out>
tmp = {value = {lval = <optimized out>, dval = <optimized out>, counted = <optimized out>, str = <optimized out>,
arr = <optimized out>, obj = <optimized out>, res = <optimized out>, ref = <optimized out>, ast = <optimized out>,
zv = <optimized out>, ptr = <optimized out>, ce = <optimized out>, func = <optimized out>, ww = {w1 = <optimized out>,
w2 = <optimized out>}}, u1 = {type_info = <optimized out>, v = {type = <optimized out>, type_flags = <optimized out>, u = {
--Type <RET> for more, q to quit, c to continue without paging--c
extra = <optimized out>}}}, u2 = {next = <optimized out>, cache_slot = <optimized out>, opline_num = <optimized out>, lineno = <optimized out>, num_args = <optimized out>, fe_pos = <optimized out>, fe_iter_idx = <optimized out>, property_guard = <optimized out>, constant_flags = <optimized out>, extra = <optimized out>}}
ast_ref = <optimized out>
ast_is_refcounted = <optimized out>
result = <optimized out>
_z1 = <optimized out>
_z2 = <optimized out>
_gc = <optimized out>
_t = <optimized out>
#7 0x0000558eeb6ad06e in ZEND_RECV_INIT_SPEC_CONST_HANDLER () at ./Zend/zend_vm_execute.h:3736
cache_val = 0x7fb7314c14a8
default_value = 0x7fb7306424b0
arg_num = <optimized out>
param = 0x7fb736c14440
arg_num = <optimized out>
param = <optimized out>
default_value = <optimized out>
cache_val = <optimized out>
_z1 = <optimized out>
_z2 = <optimized out>
_gc = <optimized out>
_t = <optimized out>
_z1 = <optimized out>
_z2 = <optimized out>
_gc = <optimized out>
_t = <optimized out>
_z1 = <optimized out>
_z2 = <optimized out>
_gc = <optimized out>
_t = <optimized out>
_z1 = <optimized out>
_z2 = <optimized out>
_gc = <optimized out>
_t = <optimized out>
Unfortunately I was not able to construct a minimal reproducable setup, therefore I will add some context on the project and the enums classes involved.
Our php modulues:
php -m
[PHP Modules]
apcu
bcmath
calendar
Core
ctype
curl
date
dba
dom
exif
FFI
fileinfo
filter
ftp
gd
gettext
hash
iconv
igbinary
json
libxml
mbstring
openssl
pcntl
pcre
PDO
pdo_pgsql
pgsql
Phar
posix
readline
redis
Reflection
session
shmop
SimpleXML
sockets
sodium
SPL
standard
sysvmsg
sysvsem
sysvshm
tideways
tokenizer
uuid
xml
xmlreader
xmlwriter
xsl
Zend OPcache
zip
zlib
[Zend Modules]
Zend OPcache
The enum we've added:
enum BoostTargetedFeatureEnum: string implements JsonSerializable
{
use EnumSerializableTrait;
public static function getFrom(?string $value): self
{
if (!$value) {
return self::All;
}
return self::tryFrom($value) ?? self::All;
}
case GuildExpedition = 'guild_expedition';
case GuildBattleground = 'battleground';
case All = 'all';
}
<?phpThe trait we're including:
trait EnumSerializableTrait
{
public function jsonSerialize(): array
{
$value = $this->value ?? $this->name;
return ['__enum__' => $this->getEnumName(), 'value' => $value];
}
public function getEnumName(): string
{
return str_replace('Enum', '', strip_namespace($this));
}
}The BoostHintVO class mentioned in the stacktrace:
class BoostHintVO
{
public string $type;
public int $value;
public BoostTargetedFeatureEnum $targetedFeature = BoostTargetedFeatureEnum::All;
public static function create(
string $type,
int $value,
BoostTargetedFeatureEnum $target = BoostTargetedFeatureEnum::All
): BoostHintVO {
$vo = new BoostHintVO();
$vo->type = $type;
$vo->value = $value;
$vo->targetedFeature = $target;
return $vo;
}
}line 16 from the stacktrace is the line of the constructor with the reference to the ::All case.
The segfault only occurs with opcache enabled. The first request on a freshly restarted php-fpm process will succeed, the second request fails with the segfault.
The enum case All is being referenced from 30 places in our codebase, a lot of times as a default argument on an optional method parameter.
The segfault also occurred when modifying the BoostHintVO::create to accept a nullable string when we called the BoostTargetedFeatureEnum::getFrom inside the class - in case it's relevant, this static create fuction (as well as other places in the code that will reference that new enum) will get called a lot from different factories in our code that produce these VOs that are sent to clients. If there is any more context I can or should provide please do let me know!
When searching for other segfaults related to php enums I found #10914 where the faulty/changed code looks fairly similar to where it's failing according the stacktrace:
Line 1324 in badfe4f
| ZEND_HASH_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) { |
vs
ndossche@e6abc93#diff-a9c2ca78a68a3ad33d5b00dd7f1d37aa2ac438dbff79d56d06918aefaf6fe4af
This might be absolutely unrelated as I have no prior experience in php core development, however I wanted to mention it in case the causes are indeed related.
PHP Version
PHP 8.1.21
Operating System
Debian 11.7