Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix circumvented added hooks in JIT #17870

Open
wants to merge 4 commits into
base: PHP-8.4
Choose a base branch
from

Conversation

iluuu1994
Copy link
Member

The following code poses a problem in the JIT:

class A {
    public $prop = 1;
}

class B extends A {
    public $prop = 1 {
        get => parent::$prop::get() * 2;
    }
}

function test(A $a) {
    var_dump($a->prop);
}

test(new B);

The JIT would assume A::$prop in test() could be accessed directly through OBJ_PROP_NUM(). However, since child classes can add new hooks to existing properties, this assumption no longer holds.

To avoid introducing more JIT checks, a hooked property that overrides a unhooked property now results in a separate zval slot that is used instead of the parent slot. This causes the JIT to pick the slow path due to an IS_UNDEF value in the parent slot.

zend_class_entry.properties_info_table poses a problem in that zend_get_property_info_for_slot() and friends will be called using the child slot, which does not store its property info, since the parent slot already does. In this case, zend_get_property_info_for_slot() now provides a fallback that will iterate all property infos to find the correct one.

This also uncovered a bug (see Zend/tests/property_hooks/dump.phpt) where the default value of a parent property would accidentally be inherited by the child property.

Fixes GH-17376

@iluuu1994 iluuu1994 requested a review from arnaud-lb February 20, 2025 21:00
@iluuu1994 iluuu1994 changed the base branch from master to PHP-8.4 February 20, 2025 21:05
The following code poses a problem in the JIT:

```php
class A {
    public $prop = 1;
}

class B extends A {
    public $prop = 1 {
        get => parent::$prop::get() * 2;
    }
}

function test(A $a) {
    var_dump($a->prop);
}

test(new B);
```

The JIT would assume A::$prop in test() could be accessed directly
through OBJ_PROP_NUM(). However, since child classes can add new hooks
to existing properties, this assumption no longer holds.

To avoid introducing more JIT checks, a hooked property that overrides a
unhooked property now results in a separate zval slot that is used
instead of the parent slot. This causes the JIT to pick the slow path
due to an IS_UNDEF value in the parent slot.

zend_class_entry.properties_info_table poses a problem in that
zend_get_property_info_for_slot() and friends will be called using the
child slot, which does not store its property info, since the parent
slot already does. In this case, zend_get_property_info_for_slot() now
provides a fallback that will iterate all property infos to find the
correct one.

This also uncovered a bug (see Zend/tests/property_hooks/dump.phpt)
where the default value of a parent property would accidentally be
inherited by the child property.

Fixes phpGH-17376
@iluuu1994
Copy link
Member Author

I have no clue what this Windows failure is about. 😞 I can't reproduce on Linux with asan/msan/valgrind, w/wo opcache, nts/zts, etc. @cmb69 Can you help? I don't have a VM...

@cmb69
Copy link
Member

cmb69 commented Feb 21, 2025

It's a JIT issue. Running ext\random\tests\02_engine\all_serialize_user.phpt under ASan/tracing JIT:

Random\Engine\Test\TestCountingEngine32
=================================================================
==13488==ERROR: AddressSanitizer: access-violation on unknown address 0x000000000014 (pc 0x7ffce98a8fa2 bp 0x0089ff7fc6b0 sp 0x0089ff7fadb0 T0)
==13488==The signal is caused by a READ memory access.
==13488==Hint: address points to the zero page.
    #0 0x7ffce98a8fa1 in verify_readonly_and_avis C:\php-sdk\phpdev\vs17\x64\php-src\ext\opcache\jit\zend_jit_helpers.c:2716
    #1 0x7ffce989b658 in zend_jit_post_inc_typed_prop C:\php-sdk\phpdev\vs17\x64\php-src\ext\opcache\jit\zend_jit_helpers.c:2976
    #2 0x200008000e61  (<unknown module>)
    #3 0x000000007f12  (<unknown module>)
    #4 0x000000000000  (<unknown module>)
    #5 0x000000007f13  (<unknown module>)
    #6 0x1205678a1cd7  (<unknown module>)
    #7 0x1205678a1c7f  (<unknown module>)
    #8 0x11f9678c0877  (<unknown module>)
    #9 0x11f9678c073f  (<unknown module>)
    #10 0x11f9678c0877  (<unknown module>)
    #11 0x002700000027  (<unknown module>)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: access-violation C:\php-sdk\phpdev\vs17\x64\php-src\ext\opcache\jit\zend_jit_helpers.c:2716 in verify_readonly_and_avis
==13488==ABORTING

Does that help?

@iluuu1994
Copy link
Member Author

Thank you very much! It might, I'll have a look very soon.

Z_PROP_TABLE_OFFSET already gives us an index into a void* array. Thus,
we only need to multiply by sizeof(void*).
@cmb69
Copy link
Member

cmb69 commented Feb 22, 2025

Looks good. :)

@iluuu1994
Copy link
Member Author

@cmb69 Indeed, it did help 😊 Thanks again!

@iluuu1994 iluuu1994 marked this pull request as ready for review February 22, 2025 00:21
@iluuu1994 iluuu1994 requested a review from dstogov as a code owner February 22, 2025 00:21
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.

[BUG] Property hooks + enabled JIT causes unexpected results (segfault for JIT function)
3 participants