Skip to content

Fix IS_IDENTICAL_EMPTY_ARRAY specialization for an IS_CONST op1 - #23545

Open
Mrmaxmeier wants to merge 1 commit into
php:PHP-8.5from
Mrmaxmeier:fix-identical-empty-array-const
Open

Fix IS_IDENTICAL_EMPTY_ARRAY specialization for an IS_CONST op1#23545
Mrmaxmeier wants to merge 1 commit into
php:PHP-8.5from
Mrmaxmeier:fix-identical-empty-array-const

Conversation

@Mrmaxmeier

Copy link
Copy Markdown
Contributor

Hi,

we ran into an assertion failure in the VM with the fuzzer-function-jit fuzzing target:

<?php
if (A::class->p === []) {
    echo "identical\n";
}
if (A::class->p !== []) {
    echo "not identical\n";
}
echo "OK\n";

On a debug build this aborts in _get_zval_ptr_tmpvarcv(). ZEND_IS_IDENTICAL_EMPTY_ARRAY is new in 8.5, so this does not affect 8.4.

Assertion backtrace for reproducer
/out/php-fuzz-function-jit: Running 1 inputs 100 time(s) each.
Running: /testcase
php-fuzz-function-jit: /src/php-src/Zend/zend_execute.c:402: zval *_get_zval_ptr_tmpvarcv(int, znode_op, int, zend_execute_data *): Assertion `op_type == (1<<3)' failed.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==15==ERROR: AddressSanitizer: ABRT on unknown address 0x00000000000f (pc 0x7fd4f89f000b bp 0x7fd4f8b65588 sp 0x7fff42d4cc30 T0)
SCARINESS: 10 (signal)
    #0 0x7fd4f89f000b in raise (/lib/x86_64-linux-gnu/libc.so.6+0x4300b) (BuildId: 5792732f783158c66fb4f3756458ca24e46e827d)
    #1 0x7fd4f89cf858 in abort (/lib/x86_64-linux-gnu/libc.so.6+0x22858) (BuildId: 5792732f783158c66fb4f3756458ca24e46e827d)
    #2 0x7fd4f89cf728  (/lib/x86_64-linux-gnu/libc.so.6+0x22728) (BuildId: 5792732f783158c66fb4f3756458ca24e46e827d)
    #3 0x7fd4f89e0fd5 in __assert_fail (/lib/x86_64-linux-gnu/libc.so.6+0x33fd5) (BuildId: 5792732f783158c66fb4f3756458ca24e46e827d)
    #4 0x56412dbacdc3 in _get_zval_ptr_tmpvarcv /src/php-src/Zend/zend_execute.c:402:3
    #5 0x56412dbacdc3 in ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ_HANDLER /src/php-src/Zend/zend_vm_execute.h:14529:8
    #6 0x56412df140bb in fuzzer_execute_ex /src/php-src/sapi/fuzzer/fuzzer-execute-common.h:65:12
    #7 0x56412da3813d in zend_execute /src/php-src/Zend/zend_vm_execute.h:115989:2
    #8 0x56412df153bf in fuzzer_do_request_from_buffer /src/php-src/sapi/fuzzer/fuzzer-sapi.c:293:5
    #9 0x56412df13954 in LLVMFuzzerTestOneInput /src/php-src/sapi/fuzzer/fuzzer-function-jit.c:32:2
    [..]

DEDUP_TOKEN: raise--abort--
SUMMARY: AddressSanitizer: ABRT (/lib/x86_64-linux-gnu/libc.so.6+0x4300b) (BuildId: 5792732f783158c66fb4f3756458ca24e46e827d) in raise
==15==ABORTING

The ZEND_IS_IDENTICAL_EMPTY_ARRAY / ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY specializations are declared for the operand pair TMPVARCV, CONST:

ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_IDENTICAL, op->op2_type == IS_CONST && (...), ZEND_IS_IDENTICAL_EMPTY_ARRAY, TMPVARCV, CONST, ...)

but the condition that selects them only ever looked at op2. The other specializations of these opcodes bail out for a CONST/CONST operand pair, this one did not. So an IS_CONST op1 still selected a handler that reads its op1 through _get_zval_ptr_tmpvarcv(), which cannot read a literal and asserts.

Getting there needs the optimizer: type inference proves the property fetch on the A::class string to be null and substitutes the constant into op1, but the comparison itself is not folded, which leaves a ZEND_IS_IDENTICAL with two IS_CONST operands for the specializer to look at.

The fix adds the missing op->op1_type != IS_CONST to the selection condition of both handlers, which is what the sibling specializations already do.

Thanks!


Found by the CISPA Fandango team while triaging findings in oss-fuzz harnesses.

The ZEND_IS_IDENTICAL_EMPTY_ARRAY / ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY
specializations are declared as TMPVARCV, CONST, but their selection condition
only looked at op2. The other specializations of these opcodes bail out for a
CONST/CONST operand pair, this one did not, so an IS_CONST op1 selected a
handler that cannot read it and _get_zval_ptr_tmpvarcv() asserted:

    if (A::class->p === []) {
    }

Type inference proves the property fetch to be null and substitutes the
constant into op1, but the comparison itself is not folded, which leaves
ZEND_IS_IDENTICAL with two IS_CONST operands.

Assisted-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant