PHP Environment
opcache
Section titled “opcache”The opcache extension mis-executes the ZEND_TYPE_CHECK opcode in at least PHP 8.0.25 and causes a vital PHP expression, is_resource($x), to curiously yield false even when $x is a valid resource. The issue occurs in the same spot in grommunio-web, and is reliably reproducible, but we do not know why exactly. g-web is a big program and attempts to replicate the problem with a smaller program have been unsuccessful to date.
mapi.so checks whether opcache.so is present and enabled, and if so, refuses operation. You must disable opcache.
Details:
We find that the Zend engine treats the PHP is_resource(...) function call specially ([1] https://github.com/php/php-src/blob/master/Zend/zend_compile.c#L4497_):
} else if (zend_string_equals_literal(lcname, "is_resource")) { return zend_compile_func_typecheck(result, args, IS_RESOURCE);and that Zend compiles it to a ZEND_TYPE_CHECK opcode with extended_value being (1 << IS_RESOURCE) ([2]).
opline = zend_emit_op_tmp(result, ZEND_TYPE_CHECK, &arg_node, NULL);if (type != _IS_BOOL) { opline->extended_value = (1 << type);} else { opline->extended_value = (1 << IS_FALSE) | (1 << IS_TRUE);}When the two lines shown in the first block are removed, the is_resource($x) expression would instead be compiled to a ZEND_INIT_FCALL opcode ([3]) and execution would eventually land in the C function corresponding to is_resource ([4]).
static inline void php_is_type(INTERNAL_FUNCTION_PARAMETERS, int type){ if (Z_TYPE_P(arg) == type) { ...Summarizing our observations:
- Unmodified Zend VM, php-opcache disabled,
is_resourcebecomesZEND_TYPE_CHECK: good - Unmodified Zend VM, php-opcache enabled,
is_resourcebecomesZEND_TYPE_CHECK: bad - Modified Zend VM, php-opcache enabled,
is_resourcebecomesZEND_INIT_FCALL: good - We conclude that php-opcache induces a problem with respect to the
ZEND_TYPE_CHECKopcode.
There is a... peculiar comment in php-opcache (MAY_BE_RESOURCE is the same as 1 << IS_RESOURCE) ([5]) that could(?) be relevant:
case ZEND_TYPE_CHECK: if (opline->extended_value == MAY_BE_RESOURCE) { // TODO: support for is_resource() ??? break; }