From cffeb677bf8499e36e320cfdb4540b89fe4e9ddc Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sat, 11 Jan 2020 23:29:31 +0100 Subject: [PATCH 1/2] Fix #79096: FFI Struct Segfault We must not assume that the size of a function's return value is at most `sizeof(ffi_arg)`, but rather have to use the size which already has been determined for the return type if it is larger than `sizeof(ffi_arg)`. --- ext/ffi/ffi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 369e6531ace37..f43ba2adc8d27 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -2613,10 +2613,11 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */ ffi_type **arg_types = NULL; void **arg_values = NULL; uint32_t n, arg_count; - ffi_arg ret; + void *ret; zend_ffi_type *arg_type; ALLOCA_FLAG(arg_types_use_heap = 0) ALLOCA_FLAG(arg_values_use_heap = 0) + ALLOCA_FLAG(ret_use_heap = 0) ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); arg_count = type->func.args ? zend_hash_num_elements(type->func.args) : 0; @@ -2704,7 +2705,8 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */ } } - ffi_call(&cif, addr, &ret, arg_values); + ret = do_alloca(MAX(ret_type->size, sizeof(ffi_arg)), ret_use_heap); + ffi_call(&cif, addr, ret, arg_values); for (n = 0; n < arg_count; n++) { if (arg_types[n]->type == FFI_TYPE_STRUCT) { @@ -2720,7 +2722,8 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */ free_alloca(arg_values, arg_values_use_heap); } - zend_ffi_cdata_to_zval(NULL, (void*)&ret, ZEND_FFI_TYPE(type->func.ret_type), BP_VAR_R, return_value, 0, 1); + zend_ffi_cdata_to_zval(NULL, ret, ZEND_FFI_TYPE(type->func.ret_type), BP_VAR_R, return_value, 0, 1); + free_alloca(ret, ret_use_heap); exit: zend_string_release(EX(func)->common.function_name); From e8fbf14ad8e52ff0a8e5d66f45aee4f6ec9faf46 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 12 Jan 2020 14:01:42 +0100 Subject: [PATCH 2/2] Add regression test To be able to have a regression test, we export the required test function from the zend-test extension, and make sure that the test can be run on different platforms regardless of whether zend-tests was built statically or dynamically. --- ext/ffi/tests/bug79096.phpt | 39 +++++++++++++++++++++++++++++++++++++ ext/zend_test/php_test.h | 7 +++++++ ext/zend_test/test.c | 9 +++++++++ 3 files changed, 55 insertions(+) create mode 100644 ext/ffi/tests/bug79096.phpt diff --git a/ext/ffi/tests/bug79096.phpt b/ext/ffi/tests/bug79096.phpt new file mode 100644 index 0000000000000..c495efc92e5a3 --- /dev/null +++ b/ext/ffi/tests/bug79096.phpt @@ -0,0 +1,39 @@ +--TEST-- +Bug #79096 (FFI Struct Segfault) +--SKIPIF-- + +--FILE-- +bug79096(); +var_dump($struct); +?> +--EXPECTF-- +object(FFI\CData:struct bug79096)#%d (2) { + ["a"]=> + int(1) + ["b"]=> + int(1) +} diff --git a/ext/zend_test/php_test.h b/ext/zend_test/php_test.h index b76155e866b5b..325484c434805 100644 --- a/ext/zend_test/php_test.h +++ b/ext/zend_test/php_test.h @@ -32,4 +32,11 @@ extern zend_module_entry zend_test_module_entry; ZEND_TSRMLS_CACHE_EXTERN() #endif +struct bug79096 { + uint64_t a; + uint64_t b; +}; + +ZEND_API struct bug79096 bug79096(void); + #endif diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index ad5f2fb44673b..b097a2412b9f5 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -319,3 +319,12 @@ ZEND_TSRMLS_CACHE_DEFINE() #endif ZEND_GET_MODULE(zend_test) #endif + +struct bug79096 bug79096(void) +{ + struct bug79096 b; + + b.a = 1; + b.b = 1; + return b; +}