Skip to content

Opcache: SCCP turns $c::$m() into CONST/CONST INIT_STATIC_METHOD_CALL with one cache slot, heap-buffer-overflow #24252

Open

Description

@waroad

With SCCP enabled and the compact-literals pass off, a dynamic static call whose class and method names are both constant-propagated reads past the function's runtime cache in ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER, and a release build segfaults.

<?php
class Foo { static function bar() { return "sb"; } }
function f() { $c = "Foo"; $m = "bar"; return $c::$m(); }
echo f(), "\n";
php -n -d opcache.enable_cli=1 -d opcache.file_update_protection=0 -d opcache.optimization_level=0xA0 static_call.php

opcache.optimization_level=0x7FFEBBFF (the default minus pass 11, 0x400) fails the same way. On an ASan build (run with USE_ZEND_ALLOC=0):

==282==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000003ed8 at pc 0x55a8609a58e4 bp 0x7ffe492d9390 sp 0x7ffe492d9380
READ of size 8 at 0x502000003ed8 thread T0
    #0 0x55a8609a58e3 in ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER Zend/zend_vm_execute.h:7558
    #1 0x55a860c28b6f in execute_ex Zend/zend_vm_execute.h:111556
    #2 0x55a860c71830 in zend_execute Zend/zend_vm_execute.h:116021

0x502000003ed8 is located 0 bytes to the right of 8-byte region [0x502000003ed0,0x502000003ed8)
allocated by thread T0 here:
    #4 0x55a86091962d in init_func_run_time_cache_i Zend/zend_execute.c:4511
    #6 0x55a86097b67f in ZEND_INIT_FCALL_SPEC_CONST_HANDLER Zend/zend_vm_execute.h:4194

Expected sb, which is what the script prints with opcache off and at the default optimization level.

Found at commit b527e1b and still reproduces at 12df767. A release build segfaults (exit 139, no output) at both optimization levels above.

Looks like SCCP replaces the two operands one at a time: zend_optimizer_update_op1_const() (Zend/Optimizer/zend_optimizer.c:318-326) sees op2 still a CV and allocates one slot, then zend_optimizer_update_op2_const() (zend_optimizer.c:508-514) sees op1 already CONST and allocates nothing, while the CONST/CONST handler uses two slots (class and function). The default level hides it because pass 11 reallocates all cache slots.

I can send a patch if that helps.

Found with an LLM-based testing tool. As CONTRIBUTING.md asks, the paragraphs in > quotes were written with Claude; I ran every command myself.

Activity

  1. ndossche commented on Oct 11, 2026

    @ndossche
    Member

    @waroad I also found this after your first issue. The fix is already open. Is it possible your LLM has been reading the fix PR and is just rediscovering the same issues?
    Please next time first check if #24237 covers this

  2. linked a pull request that will close this issueFix a bunch of cache slot optimizer bugs #24237on Oct 11, 2026
  3. waroad commented on Oct 11, 2026

    @waroad
    Author

    No, it was found on its own: the test-generation run that hit it ran on Oct 9, before #24237 was opened. But I should have checked whether your open fix already covered it before filing, and I didn't. That's my mistake, sorry for the extra work. I'm adding a check against open fix PRs before anything else gets filed, so this won't happen again. Feel free to close this as covered by #24237.

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