Skip to content

OPcache: file cache loses attributes on constants on 32-bit builds #24745

Closed

Description

@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

#[Deprecated(message: 'bar')]
const FOO = 1;

test.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):

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:

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

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)

Activity

  1. mho22 commented on Oct 26, 2026

    @mho22
    ContributorAuthor

    The same crash happens on arm32v7/php:8.5-cli. On an i386 build of php-8.5.12 from source, the fix above makes both runs print the expected output.

  2. ndossche commented on Oct 26, 2026

    @ndossche
    Member

    Might be fixed on master by #24392

  3. added 3 commits that reference this issue on Oct 26, 2026
    a2052f3
    fdcca08
    b7567e5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions