php / php/php-src

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

未关闭
#12,910 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

Bug Extension: ffi Status: Verified
主要语言
C
星标
40.4k
派生
8.2k
平均合并
2 天 13 小时
30 天内合并 PR
96

描述

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

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

首先在 PHP 8.3 的调试构建中复现基于类的 FFI 调用和未解析的 FAKE_FUNCTION 情况。检查 ext/ffi/ffi.c 中第 1631 行附近的 zend_ffi_ctype_name,以及第 6440、6559、6628 行报告的泄漏位置。完成的标准是:不兼容的 Struct 参数在不触发断言的情况下引发正常的 FFI 异常,并且失败的函数解析不会在调试构建中留下内存泄漏。

由索引模型根据 Issue 内容生成。

评估

技术栈
c, php
领域
backend
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
停滞
描述清晰度
基本清楚
新手友好度
35/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。