Skip to content

Turbo 2.3.0 incompatible with Tideways #15407

Closed
phpstan/phpstan-src
#6757

Description

@DuckThom

Bug report

PHP Version: PHP 8.3.6 (installed by apt on Ubuntu 24.04 LTS)

We noticed that on specifically PHP 8.3 we are getting segmentation faults when CI is trying to run PHPStan.

In our custom PHP 8.3 container (based on ubuntu:24.04), I managed to get the following backtrace from GDB:

(gdb) bt
#0  __strlen_evex () at ../sysdeps/x86_64/multiarch/strlen-evex-base.S:81
#1  0x000056143b1bfa26 in ?? ()
#2  0x000056143b228efd in zend_vspprintf ()
#3  0x000056143b0233b5 in zend_argument_error_variadic ()
#4  0x000056143b023599 in zend_argument_type_error ()
#5  0x00007ff031b5e3a9 in requireTypeArguments(unsigned int, _zval_struct*) ()
   from /work/vendor/phpstan/phpstan/turbo-ext/linux-gnu-x86_64/phpstan_turbo-8.3.so
#6  0x00007ff03561beb0 in ?? ()
#7  0x00007ff03561bd10 in ?? ()
#8  0x0000000000000002 in ?? ()
#9  0x00007ff031b646e5 in pt_register_type_combinator()::{lambda(_zend_execute_data*, _zval_struct*)#7}::_FUN(_zend_execute_data*, _zval_struct*) () from /work/vendor/phpstan/phpstan/turbo-ext/linux-gnu-x86_64/phpstan_turbo-8.3.so
#10 0x00007fffa32555f0 in ?? ()
#11 0x000056143b2cfd4b in ?? ()

Using the base ubuntu:24.04 container to install php + tideways manually, I don't get a segmentation fault but I do get an internal error from PHPStan:

root@e12a7f4b4cc6:/work# vendor/bin/phpstan analyse .
  0/11 [░░░░░░░░░░░░░░░░░░░░░░░░░░░░]   0%
In FileHelper.php line 67:
                                                                      
  strlen(): Argument #1 ($string) must be of type string, null given  
                                                                      

analyse [-c|--configuration CONFIGURATION] [-l|--level LEVEL] [--no-progress] [--debug] [-a|--autoload-file AUTOLOAD-FILE] [--error-format ERROR-FORMAT] [-b|--generate-baseline [GENERATE-BASELINE]] [--allow-empty-baseline] [--memory-limit MEMORY-LIMIT] [--xdebug] [--tmp-file TMP-FILE] [--instead-of INSTEAD-OF] [--fix] [--watch] [--pro] [--fail-without-result-cache] [--] [<paths>...]

 11/11 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

 -- ------------------------------------------------------------------- 
     Error                                                              
 -- ------------------------------------------------------------------- 
     Child process error (exit code 1):  while running parallel worker  
 -- ------------------------------------------------------------------- 


                                                                                                                        
 [ERROR] Found 1 error                                                                                                  
                                                                                                                        

⚠️  Result is incomplete because of severe errors. ⚠️
   Fix these errors first and then re-run PHPStan
   to get all reported errors.

In both instances it seems to struggle with a strlen call.

The second case is reproducable with the following Dockerfile:

FROM ubuntu:24.04

WORKDIR /work

RUN apt-get update \
    && apt-get install -y wget php-cli unzip \
    && wget -qO - 'https://packages.tideways.com/key.gpg' | tee /usr/share/keyrings/tideways.asc > /dev/null \
    && echo 'deb [signed-by=/usr/share/keyrings/tideways.asc] https://packages.tideways.com/apt-packages-main any-version main' | tee /etc/apt/sources.list.d/tideways.list \
    && apt-get update \
    && apt-get install tideways-php tideways-daemon \
    && wget https://getcomposer.org/composer-2.phar \
    && php composer-2.phar require --dev phpstan/phpstan:2.3.0

RUN php vendor/bin/phpstan analyse .

When built with docker build . the build will fail with "strlen(): Argument #1 ($string) must be of type string, null given" and "Child process error (exit code 1): while running parallel worker"

If vendor/phpstan/phpstan/turbo-ext/linux-gnu-x86_64/phpstan_turbo-8.3.so is moved to, for example, /tmp then phpstan runs correctly with tideways both on our custom image and the ubuntu:24.04 one.

If I downgrade to the latest PHPStan 2.2 release, the issue is also gone in both cases.

Code snippet that reproduces the problem

No response

Expected output

PHPStan runs correctlywith Turbo and Tideways without throwing "strlen(): Argument #1 ($string) must be of type string, null given"

Did PHPStan help you today? Did it make you happy in any way?

No response

Activity

  1. DuckThom commented on Oct 13, 2026

    @DuckThom
    Author

    I have done some more digging by adding debug code to <turbo-ext>/src/TypeCombinator.cpp as that is where the segmentation faults are originating from.

    The segfault is actually triggered by this line in Composer: https://github.com/composer/composer/blob/main/src/Composer/PHPStan/ConfigReturnTypeExtension.php#L185

    Adding a var_dump right before it reveals the following value for $types:

    array(2) {
      [0]=>
      object(PHPStan\Type\StringType)#1465 (0) {
      }
      [1]=>
      object(PHPStan\Type\Constant\ConstantBooleanType)#1464 (1) {
        ["value":"PHPStan\Type\Constant\ConstantBooleanType":private]=>
        bool(false)
      }
    }
    

    However, inside the turbo extension, with the following debug lines added to turbo-ext/src/TypeCombinator.cpp:4353:

            cls.method(sigs::union_, [](INTERNAL_FUNCTION_PARAMETERS) {
                    zval *types;
                    uint32_t count;
    
                    ZEND_PARSE_PARAMETERS_START(0, -1)
                            Z_PARAM_VARIADIC('*', types, count)
                    ZEND_PARSE_PARAMETERS_END();
    
                    fprintf(stderr, "\n--- [STACK INSPECTION] ---\n");
                    fprintf(stderr, "execute_data:      %p\n", (void*)execute_data);
                    fprintf(stderr, "prev_execute_data: %p\n", (void*)execute_data->prev_execute_data);
                    fprintf(stderr, "ZEND_CALL_ARG 1:   %p\n", (void*)ZEND_CALL_ARG(execute_data, 1));
                    fprintf(stderr, "ZEND_CALL_ARG 2:   %p\n", (void*)ZEND_CALL_ARG(execute_data, 2));
                    fprintf(stderr, "types pointer:     %p (count=%u)\n", (void*)types, count);
    
                    // Dump 6 zval slots around types to locate #1464
                    for (int k = -1; k <= (int)count + 1; k++) {
                            zval *zv = &types[k];
                            fprintf(stderr, "  slot[%2d] at %p: type=%u", k, (void*)zv, Z_TYPE_P(zv));
                            if (Z_TYPE_P(zv) == IS_OBJECT) {
                                    zend_object *obj = Z_OBJ_P(zv);
                                    fprintf(stderr, " (OBJECT obj=%p, handle=%u)", (void*)obj, obj ? obj->handle : 0);
                            }
                            fprintf(stderr, "\n");
                    }
                    fprintf(stderr, "--------------------------\n\n");
    
                    if (UNEXPECTED(!requireTypeArguments(count, types))) RETURN_THROWS();
                    PT_RETURN_VAL(TypeCombinator::union_(count, types));
            });

    The following is echoed:

    --- [STACK INSPECTION] ---
    execute_data:      0x7fa4a7c16410
    prev_execute_data: 0x7fa4a7c162b0
    ZEND_CALL_ARG 1:   0x7fa4a7c16460
    ZEND_CALL_ARG 2:   0x7fa4a7c16470
    types pointer:     0x7fa4a7c16460 (count=2)
      slot[-1] at 0x7fa4a7c16450: type=0
      slot[ 0] at 0x7fa4a7c16460: type=8 (OBJECT obj=0x7fa4a373c200, handle=1465)
      slot[ 1] at 0x7fa4a7c16470: type=8 (OBJECT obj=0x7fa4a7c162b0, handle=0)
      slot[ 2] at 0x7fa4a7c16480: type=4
      slot[ 3] at 0x7fa4a7c16490: type=4
    --------------------------
    

    Here, the object pointer for object(PHPStan\Type\Constant\ConstantBooleanType)#1464 is suddenly replaced with the prev_execute_data pointer. Presumably because Tideways is also wrapping internal function calls, messing with the stack.

    While I am not sure if this conflict is easily fixable, I do think the turbo extension might need an extra guard against this so it doesn't completely crash the php process if the object name cannot be resolved.

  2. staabm commented on Oct 13, 2026

    @staabm
    Contributor

    phpstan is already doing a auto-restart when xdebug is present. maybe we should just restart for tideways extension as well

  3. ondrejmirtes commented on Oct 13, 2026

    @ondrejmirtes
    Member

    Yes, and pcov 😊

  4. staabm commented on Oct 22, 2026

    @staabm
    Contributor

    //cc @TimWolla just fyi, in case there is room for improvement on the tideways end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions