Skip to content

FFI Incompatible types when assigning to type 'void(*)()' from type 'void(*)()' #9090

Closed

Description

@adsr

Description

It appears we're unable to assign a function pointer via FFI:

$ cat ffibug.c 
#include <stdio.h>

void func() {
    printf("hello\n");
}

void (*func_ptr)() = NULL;

void call_func() {
    if (func_ptr != NULL) func_ptr();
}
$ gcc -shared -fPIC -o libffibug.so ffibug.c
$ php -v
PHP 8.2.0-dev (cli) (built: Jul 19 2022 20:25:19) (NTS DEBUG)
Copyright (c) The PHP Group
Zend Engine v4.2.0-dev, Copyright (c) Zend Technologies
$ php -a
Interactive shell

php > $ffi = FFI::cdef('void func(); void (*func_ptr)(); void call_func();', __DIR__ . '/libffibug.so');
php > $ffi->call_func();
php > $ffi->func_ptr = $ffi->func;

Warning: Uncaught FFI\Exception: Incompatible types when assigning to type 'void(*)()' from type 'void(*)()' in php shell code:1
Stack trace:
#0 {main}
  thrown in php shell code on line 1
php > 

The following patch seems to fix:

diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c
index cb82c3a3c3..3cd568826f 100644
--- a/ext/ffi/ffi.c
+++ b/ext/ffi/ffi.c
@@ -267,7 +267,8 @@ static bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *
 				dst_type = ZEND_FFI_TYPE(dst_type->pointer.type);
 				src_type = ZEND_FFI_TYPE(src_type->pointer.type);
 				if (dst_type->kind == ZEND_FFI_TYPE_VOID ||
-				    src_type->kind == ZEND_FFI_TYPE_VOID) {
+				    src_type->kind == ZEND_FFI_TYPE_VOID ||
+				    src_type->kind == dst_type->kind) {
 				    return 1;
 				}
 			} else if (dst_type->kind == ZEND_FFI_TYPE_ARRAY &&
$ php -a
Interactive shell

php > $ffi = FFI::cdef('void func(); void (*func_ptr)(); void call_func();', __DIR__ . '/libffibug.so');
php > $ffi->call_func();
php > $ffi->func_ptr = $ffi->func;
php > $ffi->call_func();
hello
php > 
$ 

I'm not familiar with FFI internals so could use a review. FFI tests continue to pass at least. If it looks ok I can follow up with a PR + phpt.

PHP Version

HEAD

Operating System

Debian testing

Activity

  1. cmb69 commented on Jul 22, 2022

    @cmb69
    Member

    Hmm, I think we could do that, but that would allow to assign any function pointer to any other. So we would not only ignore the return and parameter types, but also the calling convention. That would seriously undermine the type safety of FFI. To wit:

    $ cat ffibug.c 
    #include <stdio.h>
    
    void func(char *str) {
        printf("hello %s\n");
    }
    
    void (*func_ptr)() = NULL;
    
    void call_func() {
        if (func_ptr != NULL) func_ptr();
    }
    $ gcc -shared -fPIC -o libffibug.so ffibug.c
    $ php -v
    PHP 8.2.0-dev (cli) (built: Jul 19 2022 20:25:19) (NTS DEBUG)
    Copyright (c) The PHP Group
    Zend Engine v4.2.0-dev, Copyright (c) Zend Technologies
    $ php -a
    Interactive shell
    
    php > $ffi = FFI::cdef('void func(char *); void (*func_ptr)(); void call_func();', __DIR__ . '/libffibug.so');
    php > $ffi->func_ptr = $ffi->func;
    php > $ffi->call_func();
    
    Segmentation fault (core dumped)
    

    If we want to support function pointer compatibility (what looks sensible), we should also check the details (src_type->func and dst_type->func).

  2. adsr commented on Jul 22, 2022

    @adsr
    ContributorAuthor

    Yes good idea @cmb69. I'll submit a PR incorporating that.

  3. adsr commented on Jul 22, 2022

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

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