From: Christoph M. Becker Date: Sun, 12 Jan 2020 13:01:42 +0000 (+0100) Subject: Fix #79096: FFI Struct Segfault X-Git-Tag: php-7.4.7RC1~311 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=05f3cd23ed61d800a861f2dd057ed56e783ea6f1;p=php 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)`. 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. --- diff --git a/NEWS b/NEWS index ba070d30ae..25fd34bd7c 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ PHP NEWS . Fixed bug #79078 (Hypothetical use-after-free in curl_multi_add_handle()). (cmb) +- FFI: + . Fixed bug #79096 (FFI Struct Segfault). (cmb) + - MySQLnd: . Fixed bug #79084 (mysqlnd may fetch wrong column indexes with MYSQLI_BOTH). (cmb) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 369e6531ac..f43ba2adc8 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); diff --git a/ext/ffi/tests/bug79096.phpt b/ext/ffi/tests/bug79096.phpt new file mode 100644 index 0000000000..c495efc92e --- /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 b76155e866..325484c434 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 ad5f2fb446..b097a2412b 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; +}