Skip to content

[FFI] Segfault/memory leak on incorrect use of FFI definitions #12910

Open
@iggyvolz

Description

@iggyvolz

Description

The following code:

<?php

class MyClass
{
    public static function do(): void
    {
        $ffi = FFI::cdef("struct struct1{int x;};struct struct2{int y;};void takesStruct1(struct struct1*);", __DIR__ . "/a.out");
        $struct2 = $ffi->new("struct struct2");
        $ffi->takesStruct1(FFI::addr($struct2));
    }
}

MyClass::do();

With the following C source for a.out (compile with cc -shared test.c):

#include <stdio.h>
struct struct1{int x; int y;};
struct struct2{int x;};
void takesStruct1(struct struct1*){printf("I took a struct1!");}

Resulted in this output:

php: /home/iggyvolz/php-src/ext/ffi/ffi.c:1631: zend_ffi_ctype_name: Assertion `0' failed.
Aborted

With the following gdb backtrace:

#0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0)   
    at ./nptl/pthread_kill.c:44
#1  0x00007ffff6bfcd9f in __pthread_kill_internal (signo=6, threadid=<optimized out>)
    at ./nptl/pthread_kill.c:78
#2  0x00007ffff6badf32 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#3  0x00007ffff6b98472 in __GI_abort () at ./stdlib/abort.c:79
#4  0x00007ffff6b98395 in __assert_fail_base (
    fmt=0x7ffff6d0ca90 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n",
    assertion=assertion@entry=0x555555e9582e "0",
    file=file@entry=0x555555e95188 "/home/iggyvolz/php-src/ext/ffi/ffi.c", line=line@entry=1631,
    function=function@entry=0x555555e97ca0 <__PRETTY_FUNCTION__.58> "zend_ffi_ctype_name")
    at ./assert/assert.c:92
#5  0x00007ffff6ba6e32 in __GI___assert_fail (assertion=0x555555e9582e "0",
    file=0x555555e95188 "/home/iggyvolz/php-src/ext/ffi/ffi.c", line=1631,
    function=0x555555e97ca0 <__PRETTY_FUNCTION__.58> "zend_ffi_ctype_name") at ./assert/assert.c:101       
#6  0x0000555555779388 in zend_ffi_ctype_name (buf=0x7fffffffa4e0, type=0x7ffff407f080)
    at /home/iggyvolz/php-src/ext/ffi/ffi.c:1631
#7  0x0000555555779932 in zend_ffi_get_class_name (prefix=0x555556dc1ad0, type=0x7ffff407f300)
    at /home/iggyvolz/php-src/ext/ffi/ffi.c:1732
#8  0x00005555557799d1 in zend_ffi_cdata_get_class_name (zobj=0x7ffff407f380)
    at /home/iggyvolz/php-src/ext/ffi/ffi.c:1745
#9  0x0000555555cfaaee in _build_trace_args (arg=0x7ffff40603c8, str=0x7fffffffa720)
    at /home/iggyvolz/php-src/Zend/zend_exceptions.c:517
#10 0x0000555555cfb06d in _build_trace_string (str=0x7fffffffa720, ht=0x7ffff405c6c0, num=0)
    at /home/iggyvolz/php-src/Zend/zend_exceptions.c:576
#11 0x0000555555cfb235 in zend_trace_to_string (trace=0x7ffff405c660, include_main=true)
    at /home/iggyvolz/php-src/Zend/zend_exceptions.c:602
#12 0x0000555555cfb3e8 in zim_Exception_getTraceAsString (execute_data=0x7ffff4016070,
    return_value=0x7fffffffaa90) at /home/iggyvolz/php-src/Zend/zend_exceptions.c:631
#13 0x0000555555c07d8c in zend_call_function (fci=0x7fffffffaa50, fci_cache=0x7fffffffa830)
    at /home/iggyvolz/php-src/Zend/zend_execute_API.c:970
#14 0x0000555555cfb6da in zim_Exception___toString (execute_data=0x7ffff4016020, 
    return_value=0x7fffffffaed0) at /home/iggyvolz/php-src/Zend/zend_exceptions.c:676
#15 0x0000555555c07d8c in zend_call_function (fci=0x7fffffffadc0, fci_cache=0x7fffffffad90)
    at /home/iggyvolz/php-src/Zend/zend_execute_API.c:970
#16 0x0000555555c0826a in zend_call_known_function (fn=0x555556d33960, object=0x7ffff4060300, 
    called_scope=0x555556dc0170, retval_ptr=0x7fffffffaed0, param_count=0, params=0x0, named_params=0x0)
    at /home/iggyvolz/php-src/Zend/zend_execute_API.c:1051
#17 0x0000555555cf7b95 in zend_call_known_instance_method (fn=0x555556d33960, object=0x7ffff4060300, 
    retval_ptr=0x7fffffffaed0, param_count=0, params=0x0) at /home/iggyvolz/php-src/Zend/zend_API.h:853    
#18 0x0000555555cf7bcf in zend_call_known_instance_method_with_0_params (fn=0x555556d33960,
    object=0x7ffff4060300, retval_ptr=0x7fffffffaed0) at /home/iggyvolz/php-src/Zend/zend_API.h:859        
#19 0x0000555555cfc8c0 in zend_exception_error (ex=0x7ffff4060300, severity=1)
    at /home/iggyvolz/php-src/Zend/zend_exceptions.c:921
#20 0x0000555555c243e1 in zend_execute_scripts (type=8, retval=0x0, file_count=3)
    at /home/iggyvolz/php-src/Zend/zend.c:1888
#21 0x0000555555b61ee2 in php_execute_script (primary_file=0x7fffffffd4f0)
    at /home/iggyvolz/php-src/main/main.c:2501
#22 0x0000555555dae79d in do_cli (argc=2, argv=0x555556c29550)
    at /home/iggyvolz/php-src/sapi/cli/php_cli.c:966
#23 0x0000555555daf56f in main (argc=2, argv=0x555556c29550)
    at /home/iggyvolz/php-src/sapi/cli/php_cli.c:1340

But when I run with this (should-be-equivalent) PHP code:

<?php
$ffi = FFI::cdef("struct struct1{int x;};struct struct2{int y;};void takesStruct1(struct struct1*);", __DIR__ . "/a.out");
$struct2 = $ffi->new("struct struct2");
$ffi->takesStruct1(FFI::addr($struct2));

I receive the correct output:

Fatal error: Uncaught FFI\Exception: Passing incompatible argument 1 of C function 'takesStruct1', expecting 'struct struct1*', found 'struct struct2*' in /home/iggyvolz/phpwayland/testsegfault.php:17
Stack trace:
#0 /.../testsegfault.php(17): FFI->takesStruct1(Object(FFI\CData:struct struct2*))    
#1 {main}
  thrown in /home/iggyvolz/phpwayland/testsegfault.php on line 17

While debugging this issue, I also found a similar-seeming issue with the following class:

<?php
FFI::cdef("void FAKE_FUNCTION();");

Returns a memory leak in the debug build of PHP:

Fatal error: Uncaught FFI\Exception: Failed resolving C function 'FAKE_FUNCTION' in /home/iggyvolz/phpwayland/testmemleak.php:2
Stack trace:
#0 /home/iggyvolz/phpwayland/testmemleak.php(2): FFI::cdef('void FAKE_FUNCT...')
#1 {main}
  thrown in /home/iggyvolz/phpwayland/testmemleak.php on line 2
[Fri Dec  8 07:48:04 2023]  Script:  '/home/iggyvolz/phpwayland/testmemleak.php'
/home/iggyvolz/php-src/Zend/zend_string.h(174) :  Freeing 0x00007f7aece02dc0 (40 bytes), script=/home/iggyvolz/phpwayland/testmemleak.php
[Fri Dec  8 07:48:04 2023]  Script:  '/home/iggyvolz/phpwayland/testmemleak.php'
/home/iggyvolz/php-src/ext/ffi/ffi.c(6628) :  Freeing 0x00007f7aece5b118 (24 bytes), script=/home/iggyvolz/phpwayland/testmemleak.php
[Fri Dec  8 07:48:04 2023]  Script:  '/home/iggyvolz/phpwayland/testmemleak.php'
/home/iggyvolz/php-src/ext/ffi/ffi.c(6559) :  Freeing 0x00007f7aece5c540 (56 bytes), script=/home/iggyvolz/phpwayland/testmemleak.php
[Fri Dec  8 07:48:04 2023]  Script:  '/home/iggyvolz/phpwayland/testmemleak.php'
/home/iggyvolz/php-src/Zend/zend_hash.c(177) :  Freeing 0x00007f7aece6c300 (320 bytes), script=/home/iggyvolz/phpwayland/testmemleak.php
[Fri Dec  8 07:48:04 2023]  Script:  '/home/iggyvolz/phpwayland/testmemleak.php'
/home/iggyvolz/php-src/ext/ffi/ffi.c(6440) :  Freeing 0x00007f7aece7e000 (88 bytes), script=/home/iggyvolz/phpwayland/testmemleak.php
=== Total 5 memory leaks detected ===

Not sure whether this is a different bug or just the same underlying issue manifesting differently, but they feel similar enough that my suspicion is the latter (I can split this into a separate issue if requested).

PHP Version

PHP 8.3.0

Operating System

Debian 12

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions