Project

Profile

Help

Bug #6197

open

PHP - SIGSEGV in zend_mm_alloc_small or in Java_java_lang_ProcessEnvironment_environ

Added by Thibault Liardon 9 days ago. Updated 2 days ago.

Status:
New
Priority:
Normal
Category:
PHP Extension function
Start date:
2023-09-15
Due date:
% Done:

0%

Estimated time:
Found in version:
12.3
Fixed in version:
Platforms:

Description

Hi there,

I am trying to use Saxon to run XSLT transformations from PHP. I am encountering systematic crashes when using the PHP extension with code that exercises PHP's autoloading features or is more than a trivial example script (Composer, Symfony, PHPUnit, or a simple autoloader).

What I did:

  1. Use PHP 8.2.10 on Fedora 38 x86-64 and Saxon 12.3 PHP extension.
  2. I built the PHP extension myself, you can find the RPM package to install it and the spec file used there: http://tinyurl.com/5n8v9jyz (my goal is to package it so that other people and myself can install it easily). And here is the repository: https://gitlab.com/saxon-packaging/saxon-package/-/tree/php/saxonphp?ref_type=heads
  3. I wrote a simple test script that loads Composer and creates a new instance of Saxon and another class from a library:
<?php

require_once __DIR__ . '/vendor/autoload.php';

$processor = new \Saxon\SaxonProcessor();
$input = file_get_contents(__DIR__.'/input.xml');
$xdmDoc = $processor->parseXmlFromString($input);
$processor = $processor->newXslt30Processor();

$compiled = $processor->compileFromString(
   file_get_contents(__DIR__.'/template.xslt')
);
$args = [];
$result = $compiled->transformToString($xdmDoc);

echo $result;

echo "\n";
$r = new Symfony\Component\HttpFoundation\Response();
echo var_dump($r);

What I found: Because of the reassignment between $processor on line 8, I get a SIGSEGV.

I then opened an issue on PHP's issue tracker: https://github.com/php/php-src/issues/12124

With their helpfull suggestions, I narrowed the set of conditions that trigger the SIGSEGV:

Reassignment Composer Simple autoloader Symfony Result
X X KO
X X KO
X X X KO
X OK
X X KO
X OK
X OK

Here is the GDB backtrace:

(gdb) bt
#0  zend_mm_alloc_small (bin_num=5, heap=0x7ffff7200040) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_alloc.c:1313
#1  zend_mm_alloc_heap (size=<optimized out>, heap=0x7ffff7200040) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_alloc.c:1384
#2  _emalloc (size=<optimized out>) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_alloc.c:2601
#3  0x00005555557d057a in zend_string_alloc (persistent=false, len=20) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_string.h:152
#4  zend_string_init (persistent=false, len=20, 
    str=0x7ffff73413ce "HTTP_PARTIAL_CONTENT = 206;\n    public const HTTP_MULTI_STATUS = 207;          // RFC4918\n    public const HTTP_ALREADY_REPORTED = 208;      // RFC5842\n    public const HTTP_IM_USED = 226;", ' ' <repeats 12 times>...) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_string.h:174
#5  lex_scan (zendlval=zendlval@entry=0x7fffffff9160, elem=0x7fffffff91f8) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_language_scanner.c:9818
#6  0x00005555557e72a0 in zendlex (elem=elem@entry=0x7fffffff91f8) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_compile.c:1802
#7  0x00005555557c7883 in zendparse () at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_language_parser.c:4758
#8  0x00005555557ca656 in zend_compile (type=type@entry=2) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_language_scanner.c:601
#9  0x00005555557cbdff in compile_file (file_handle=0x7fffffff9e80, type=2) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_language_scanner.c:655
#10 0x00007fffe32b9256 in phar_compile_file (file_handle=0x7fffffff9e80, type=2) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/ext/phar/phar.c:3355
#11 0x00005555557cbec1 in compile_filename (type=type@entry=2, filename=filename@entry=0x7ffff7255150) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_language_scanner.c:706
#12 0x0000555555841f8f in zend_include_or_eval (inc_filename_zv=<optimized out>, type=2) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_execute.c:4799
#13 0x0000555555850f6f in ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER () at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_vm_execute.h:38960
#14 0x0000555555879886 in execute_ex (ex=0x193600) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_vm_execute.h:59407
--Type <RET> for more, q to quit, c to continue without paging--
#15 0x00005555557fdbf8 in zend_call_function (fci=fci@entry=0x7fffffffa1c0, fci_cache=<optimized out>, fci_cache@entry=0x7fffffffa1a0) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_execute_API.c:949
#16 0x00005555557fdf87 in zend_call_known_function (fn=0x7ffff7204cd8, object=<optimized out>, called_scope=<optimized out>, retval_ptr=retval_ptr@entry=0x0, param_count=param_count@entry=1, params=params@entry=0x7fffffffa250, 
    named_params=0x0) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_execute_API.c:1043
#17 0x00005555556fba15 in spl_perform_autoload (class_name=0x7ffff727b0f0, lc_name=0x7ffff727b190) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/ext/spl/php_spl.c:445
#18 0x00005555557fcd77 in zend_lookup_class_ex (name=name@entry=0x7ffff727b0f0, key=0x7ffff727b190, flags=flags@entry=512) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_execute_API.c:1209
#19 0x00005555557fe3ea in zend_fetch_class_by_name (class_name=0x7ffff727b0f0, key=<optimized out>, fetch_type=fetch_type@entry=512) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_execute_API.c:1705
#20 0x000055555584dbb7 in ZEND_NEW_SPEC_CONST_UNUSED_HANDLER () at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_vm_execute.h:10277
#21 0x0000555555878190 in execute_ex (ex=0x193600) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_vm_execute.h:56949
#22 0x0000555555881e11 in zend_execute (op_array=0x7ffff7290000, return_value=<optimized out>) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend_vm_execute.h:60408
#23 0x000055555580cdeb in zend_execute_scripts (type=type@entry=8, retval=retval@entry=0x0, file_count=file_count@entry=3) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/Zend/zend.c:1833
#24 0x00005555557a3bda in php_execute_script (primary_file=primary_file@entry=0x7fffffffc940) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/main/main.c:2542
#25 0x00005555558f9e7b in do_cli (argc=2, argv=0x555555e10900) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/sapi/cli/php_cli.c:964
#26 0x00005555556413a9 in main (argc=2, argv=0x555555e10900) at /usr/src/debug/php-8.2.10-1.fc38.x86_64/sapi/cli/php_cli.c:1333

Here the zbactrace:

(gdb) zbacktrace
[0x7ffff72132c0] Composer\Autoload\includeFile("/home/u1/code/test2/vendor/composer/../symfony/http-foundation/Response.php") /home/u1/code/test2/vendor/composer/ClassLoader.php:571 
[0x7ffff7213220] Composer\Autoload\ClassLoader->loadClass("Symfony\Component\HttpFoundation\Response") /home/u1/code/test2/vendor/composer/ClassLoader.php:428 
[0x7ffff7213020] (main) /home/u1/code/test2/simple_test_ko.php:17 

Finally, I built PHP from source with debug enabled, as well as Saxon PHP extension:

PHP 8.2.10 (cli) (built: Sep  6 2023 12:02:56) (NTS DEBUG)
Copyright (c) The PHP Group
Zend Engine v4.2.10, Copyright (c) Zend Technologies

But this time, even the case without reassignment and without composer with the simple autoloader did not work:


// Registers a new loader.
spl_autoload_register(function ($class_name) {
    echo sprintf(">> Autoloading '%s' <<", $class_name);
    require_once __DIR__ . '/SimpleClass.php';
});

$processor = new \Saxon\SaxonProcessor();
$input = file_get_contents(__DIR__.'/input.xml');
$xdmDoc = $processor->parseXmlFromString($input);
$processor3 = $processor->newXslt30Processor();

$compiled = $processor3->compileFromString(
    file_get_contents(__DIR__.'/template.xslt')
);
$args = [];
$result = $compiled->transformToString($xdmDoc);

echo $result;

echo "\n";
$o = new Dummy\Ns1\SimpleClass('Hello');
echo "\n";
$o->write();
echo "\n";
echo var_dump($o);
New Thread 0x7fffe4fff6c0 (LWP 254722)]

Thread 1 "php" received signal SIGSEGV, Segmentation fault.
0x00007fffe851eac7 in Java_java_lang_ProcessEnvironment_environ () from /lib64/libsaxon-hec-12.3.so
(gdb) bt
#0  0x00007fffe851eac7 in Java_java_lang_ProcessEnvironment_environ () from /lib64/libsaxon-hec-12.3.so
#1  0x00007fffe717d8da in ?? () from /lib64/libsaxon-hec-12.3.so
#2  0x3030303030303030 in ?? ()
#3  0x3038203130203130 in ?? ()
#4  0x00007fffe717d8c7 in ?? () from /lib64/libsaxon-hec-12.3.so
#5  0x00007fffffffac50 in ?? ()
#6  0x0000000000000000 in ?? ()
(gdb) source ./.gdbinit 
(gdb) zbacktrace
[0x7ffff7616250] Saxon\SaxonProcessor->__construct() [internal function]
[0x7ffff7616020] (main) /home/tli/perso/test2/simple_test_ko2.php:9 
(gdb) 

My opinion and that of the people that commented on the issue on GitHub so far is that we can exclude a problem in PHP's core.

Does anyone here encountered something similar when using Saxon with PHP ?

Actions #1

Updated by Martin Honnen 8 days ago

Do you get SaxonC to run at all in that debug enabled PHP build you did? It is not quite clear whether the code not using Saxon (i.e. the autoloader and your SimpleClass) are part of the cause of the crash.

I run the other sample with a normal PHP 8.1 under Ubuntu 22.01 and checked that the reuse of the $processor variable for two different SaxonC objects indeed makes the code crash although there it looks as if SaxonC runs through and outputs its result, the crash happens later.

Anyway, you will have to wait for Saxonica's O'Neil to investigate that problem with the PHP extension.

Actions #2

Updated by O'Neil Delpratt 4 days ago

  • Category set to PHP Extension function
  • Assignee set to O'Neil Delpratt
  • Priority changed from Low to Normal

Hi,

I managed to reproduce the segmentation fault running with Symfony on my linux box. It is indeed a SaxonC bug.

The reassigning of the created SaxonProcessor object is causing a GC action on the SaxonProcessor, which the extension tries to delete later. Hence the seg error.

The following code also causes the segmentation error:

$xslt30processor = (new \Saxon\SaxonProcessor())->newXslt30Processor();

I am investigating a resolution to this problem. The workaround is to not reassign the processor variable.

Actions #3

Updated by Thibault Liardon 4 days ago

O'Neil Delpratt wrote in #note-2:

[...] I managed to reproduce the segmentation fault running with Symfony on my linux box. It is indeed a SaxonC bug. [...] I am investigating a resolution to this problem. The workaround is to not reassign the processor variable.

Hi,

From what I understand from your comment, you were able to run Saxon + Symfony + no reassignment without any error while creating new classes after?

If so, I will have to try again, because I wasn't able to produce and return a Symfony Response object while running Saxon with the following code:

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class TestController extends AbstractController
{

    // This is a non-working route, triggered by the use of SaxonProcessor
    // and the creation of a Response.
    // This works too with PHPUnit.
    #[Route('/test', name: 'app_test')]
    public function index(): Response
    {
        $processor = new \Saxon\SaxonProcessor();
        $input = file_get_contents(__DIR__.'/../../input.xml');
        $xdmDoc = $processor->parseXmlFromString($input);
        $processor3 = $processor->newXslt30Processor();
        $compiled = $processor3->compileFromString(
            file_get_contents(__DIR__.'/../../template.xslt')
        );
        $args = [];
        $result = $compiled->transformToString($xdmDoc);

        // Below is the error trigger.
        // If you add 'echo $result; exit;', the transformed result will be shown correctly.
        $response = new Response($result);

        // Never reached.
        return $response;
    }
}
Actions #4

Updated by O'Neil Delpratt 4 days ago

I ran the example in the initial bug entry to reproduce the error. I think it was running with Symfony because code: $r = new Symfony\Component\HttpFoundation\Response(); would not work otherwise. I then modified the code with the setup: Saxon + Symfony + no reassignment and it ran without error.

I tried to run the code in comment #3 but currently hitting the PHP error:

PHP Fatal error:  Uncaught Error: Class "Symfony\Bundle\FrameworkBundle\Controller\AbstractController" not found in /home/ond1/work/repository/temp/libsaxon-EEC-linux-amd64-v12.3/samples/php/example2.php:9
Stack trace:
#0 {main}
  thrown in /home/ond1/work/repository/temp/libsaxon-EEC-linux-amd64-v12.3/samples/php/example2.php on line 9
Actions #5

Updated by Thibault Liardon 2 days ago

O'Neil Delpratt wrote in #note-4:

I ran the example in the initial bug entry to reproduce the error. I think it was running with Symfony because code: $r = new Symfony\Component\HttpFoundation\Response(); would not work otherwise. I then modified the code with the setup: Saxon + Symfony + no reassignment and it ran without error.

I tried to run the code in comment #3 but currently hitting the PHP error:

PHP Fatal error:  Uncaught Error: Class "Symfony\Bundle\FrameworkBundle\Controller\AbstractController" not found in /home/ond1/work/repository/temp/libsaxon-EEC-linux-amd64-v12.3/samples/php/example2.php:9
Stack trace:
#0 {main}
  thrown in /home/ond1/work/repository/temp/libsaxon-EEC-linux-amd64-v12.3/samples/php/example2.php on line 9

Hi O'Neil,

I am sorry, I have not been clear enough in my examples with Symfony. You need to set up a whole new Symfony framework project to run them. The reason AbstractController is missing is because the whole Symfony framework dependencies and project structure is required.

I will produce soon a ContainerFile / Docker file that installs everything so anyone without prior Symfony knowledge can reproduce it.

Please register to edit this issue

Also available in: Atom PDF