[php-src] Issue #24745: OPcache: file cache loses attributes on constants on 32-bit builds

From: Date: Mon, 26 Oct 2026 14:48:07 +0000
Subject: [php-src] Issue #24745: OPcache: file cache loses attributes on constants on 32-bit builds
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-253215@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/24745 Author: mho22 ### Description I prepared this report with an LLM. I ran the reproduction and the controls myself, on the Docker images named below. The following code: foo.php: ```php <?php #[Deprecated(message: 'bar')] const FOO = 1; ``` test.php: ```php <?php require __DIR__ . '/foo.php'; var_dump((new ReflectionConstant('FOO'))->getAttributes()[0]->getArguments()); ``` Run two times on a 32-bit build: ``` php -d opcache.enable_cli=1 -d opcache.file_cache=/tmp/opcache -d opcache.file_cache_only=1 -d opcache.file_update_protection=0 test.php ``` Resulted in this output, on both runs: ``` Segmentation fault ``` But I expected this output instead: ``` array(1) { ["message"]=> string(3) "bar" } ``` I reproduced it with the i386/php:8.5-cli Docker image. The same image gives the expected output without OPcache, and with the file cache for a constant without an attribute. php:8.5-cli on amd64 gives the expected output with the file cache. The cause is in zend_file_cache_serialize_op_array() ([ext/opcache/zend_file_cache.c#L560-L565](https://github.com/php/php-src/blob/php-8.5.12/ext/opcache/zend_file_cache.c#L560-L565)): ```c if (opline->opcode == ZEND_OP_DATA && (opline-1)->opcode == ZEND_DECLARE_ATTRIBUTED_CONST ) { zval *literal = RT_CONSTANT(opline, opline->op1); SERIALIZE_ATTRIBUTES(Z_PTR_P(literal)); } ``` opline points into the serialization buffer. With ZEND_USE_ABS_CONST_ADDR (32-bit builds), RT_CONSTANT() returns opline->op1.zv. That pointer is not serialized yet, so it still points to the literal in script->mem, not to its copy in buf. SERIALIZE_ATTRIBUTES() therefore rewrites the attributes pointer of the live script and leaves a raw pointer in the buffer: - the current request loses the attributes of the constant; - the .bin file stores an invalid pointer, which every later request loads. On 64-bit builds, RT_CONSTANT() is relative to opline, so it already resolves into buf. zend_file_cache_unserialize_op_array() is correct, because it reads the literal after it unserializes op1.zv. A possible fix, with the same pattern as the GH-17733 code for ZEND_INIT_FCALL a few lines below: ```c zval *literal = RT_CONSTANT(opline, opline->op1); #if ZEND_USE_ABS_CONST_ADDR SERIALIZE_PTR(literal); UNSERIALIZE_PTR(literal); #endif SERIALIZE_ATTRIBUTES(Z_PTR_P(literal)); ``` ### PHP Version ```plain PHP 8.5.12 (cli) (built: Oct 21 2026 15:55:33) (NTS) Copyright (c) The PHP Group Built by https://github.com/docker-library/php Zend Engine v4.5.12, Copyright (c) Zend Technologies with Zend OPcache v8.5.12, Copyright (c), by Zend Technologies ``` ### Operating System Debian, i386 (i386/php:8.5-cli Docker image under qemu on macOS arm64)

« previous php.bugs (#253215) next »