elastic / apm-agent-php

Apache License 2.0
252 stars 69 forks source link

Elastic APM PHP agent causes segmentation fault and memory corruption when handling exceptions #1075

Closed intuibase closed 11 months ago

intuibase commented 11 months ago

Describe the bug Elastic APM PHP agent causes segmentation fault and memory corruption when handling exceptions starting from PHP 8.0

To Reproduce Set following PHP directives:

zend.exception_ignore_args=0
elastic_apm.capture_errors=1

and run this script

<?php

class MyArrayIterator extends ArrayIterator
{
    static protected $fail = 0;
    public $state;

    static function fail($state, $method)
    {
        if (self::$fail == $state)
        {
            throw new Exception("State $state: $method()");
        }
    }

    function __construct()
    {
        $this->state = MyArrayIterator::$fail;
        self::fail(0, __FUNCTION__);
        parent::__construct(array(1, 2));
        self::fail(1, __FUNCTION__);
    }

    function rewind(): void
    {
        self::fail(2, __FUNCTION__);
        parent::rewind();
    }

    function valid(): bool
    {
        self::fail(3, __FUNCTION__);
        return parent::valid();
    }

    function current(): mixed
    {
        self::fail(4, __FUNCTION__);
        return parent::current();
    }

    function key(): string|int|null
    {
        self::fail(5, __FUNCTION__);
        return parent::key();
    }

    function next(): void
    {
        self::fail(6, __FUNCTION__);
        parent::next();
    }

    function __destruct()
    {
        self::fail(7, __FUNCTION__);
    }

    static function test($func, $skip = null)
    {
        echo "===$func===\n";
        self::$fail = 0;
        while(self::$fail < 10)
        {
            try
            {
                var_dump($func(new MyArrayIterator()));
                break;
            }
            catch (Exception $e)
            {
                echo $e->getMessage() . "\n";
            }
            if (isset($skip[self::$fail]))
            {
                self::$fail = $skip[self::$fail];
            }
            else
            {
                self::$fail++;
            }
            try {
                $e = null;
            } catch (Exception $e) {
            }
        }
    }
}

MyArrayIterator::test('iterator_to_array');
MyArrayIterator::test('iterator_count', array(3 => 6));

?>

Expected behavior Run stable without crash