Skip to content

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

Closed

Description

@mrVrAlex

Description

Hello, I think I found some bugs here.

  • take official docker image
  • install opcache
  • set opcache.ini config as:
[opcache]
opcache.enable=1
opcache.enable_cli=1

opcache.jit_buffer_size=100M
opcache.jit=1205

The following code:

<?php

declare(strict_types=1);

interface EntityManagerInterface {
    public function persist(object $entity);
}
class EntityManager implements EntityManagerInterface {

    public function persist(object $entity)
    {
        echo "Persisting entity: " . get_class($entity) . "\n";
    }
}
abstract class AbstractDecorator implements EntityManagerInterface {
    protected $wrapped;
    public function persist(object $entity)
    {
        $this->wrapped->persist($entity);
    }
}

class EntityManagerDecorator extends AbstractDecorator {
    protected object $proxy;
    protected $wrapped {
        get {
            if (!isset($this->proxy)) {
                $this->proxy = ($this->factory)();
            }
            return $this->proxy;
        }
    }

    public function __construct(private Closure $factory)
    {
    }
}

$em = new EntityManagerDecorator(static fn() => new EntityManager());
$a = new stdClass();
$em->persist($a);
$a1 = new stdClass();
$em->persist($a1);

Case 1: SEGMENTATION FAULT

If run this code, then it will be crash on
$em->persist($a1);

Case 2: Fatal error (unexpected behaviour)
If remove in this code interface EntityManagerInterface and implements statements then it will be give fatal error:

Fatal error: Uncaught Error: Call to a member function persist() on null in /app/test.php:20
Stack trace:
#0 /app/test.php(42): AbstractDecorator->persist(Object(stdClass))
#1 {main}
  thrown in /app/test.php on line 20

If disable opcache or use tracing mode it will be work as expected:

Persisting entity: stdClass
Persisting entity: stdClass

P.S. I tried test PHP 8.4.3RC1 - bugs also exists there.

PHP Version

PHP 8.4.2

Operating System

Alpine 3.21

Activity

  1. ndossche commented on Jan 6, 2025

    @ndossche
    Member

    Simplified:

    <?php
    
    abstract class AbstractDecorator {
        protected $wrapped;
        public function persist()
        {
            var_dump($this->wrapped);
        }
    }
    
    class EntityManagerDecorator extends AbstractDecorator {
        protected $wrapped {
            get {
                return $this->proxy;
            }
        }
    
        public function __construct(private int $proxy)
        {
        }
    }
    
    $em = new EntityManagerDecorator(1234);
    $em->persist();
  2. ndossche commented on Jan 6, 2025

    @ndossche
    Member

    Probably, zend_get_known_property_info is wrong in zend_jit.c.

  3. self-assigned this
    on Jan 6, 2025
  4. ndossche commented on Jan 6, 2025

    @ndossche
    Member

    We're returning property info for the property but not taking into account that the subclasses may make the property a hook. This causes the property to be read from the property table instead of executing the hook, giving the wrong result.
    We already return NULL in zend_get_known_property_info because function JIT doesn't support hooks.
    It's not possible I think to extend the zend_get_known_property_info to also return NULL when the property may be a hook in the subclass because we don't know what classes may get loaded at runtime.
    Ideally this gets fixed by adding real property hook support in the function JIT. This doesn't seem super easy.

  5. removed their assignment
    on Jan 6, 2025
  6. mrVrAlex commented on Jan 7, 2025

    @mrVrAlex
    Author

    It seems with tracing JIT opcache.jit=on also have bug:
    Just wrap code to the cycle:

    foreach (range(1, 100) as $i) {
        call_user_func($fn);
    }

    I think after some iterations will trigger JIT tracing and hook for $wrapped will be return null

  7. changed the title [-]Property hooks & JIT function causes segfault or unexpected results [/-] [+][BUG] Property hooks & JIT causes unexpected results (segfault for JIT function)[/+] on Jan 7, 2025
  8. changed the title [-][BUG] Property hooks & JIT causes unexpected results (segfault for JIT function)[/-] [+][BUG] Property hooks + enabled JIT causes unexpected results (segfault for JIT function)[/+] on Jan 7, 2025
  9. ndossche commented on Jan 7, 2025

    @ndossche
    Member

    That makes sense, conceptually it's still the same issue I described earlier: there is no real hook codegen support for the JIT. We detect hooks, but only on the parent classes, not the child classes, and then take the slow path. So when a child class uses a hook and the parent class doesn't, the code generated for the parent class mistakingly assumes there is no hook and that breaks things.

  10. 10 remaining items

  11. added theissue type on Feb 5, 2025
  12. added a commit that references this issue on Feb 26, 2025
    376e90f
  13. iluuu1994 commented on Feb 26, 2025

    @iluuu1994
    Member

    Thanks for the patience, we finally found a reasonable solution to this issue.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions