Skip to content

Commit 05f3cd2

Browse files
committed
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.
1 parent b0cdd8c commit 05f3cd2

File tree

5 files changed

+64
-3
lines changed

5 files changed

+64
-3
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ PHP NEWS
66
. Fixed bug #79078 (Hypothetical use-after-free in curl_multi_add_handle()).
77
(cmb)
88

9+
- FFI:
10+
. Fixed bug #79096 (FFI Struct Segfault). (cmb)
11+
912
- MySQLnd:
1013
. Fixed bug #79084 (mysqlnd may fetch wrong column indexes with MYSQLI_BOTH).
1114
(cmb)

ext/ffi/ffi.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2613,10 +2613,11 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */
26132613
ffi_type **arg_types = NULL;
26142614
void **arg_values = NULL;
26152615
uint32_t n, arg_count;
2616-
ffi_arg ret;
2616+
void *ret;
26172617
zend_ffi_type *arg_type;
26182618
ALLOCA_FLAG(arg_types_use_heap = 0)
26192619
ALLOCA_FLAG(arg_values_use_heap = 0)
2620+
ALLOCA_FLAG(ret_use_heap = 0)
26202621

26212622
ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC);
26222623
arg_count = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
@@ -2704,7 +2705,8 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */
27042705
}
27052706
}
27062707

2707-
ffi_call(&cif, addr, &ret, arg_values);
2708+
ret = do_alloca(MAX(ret_type->size, sizeof(ffi_arg)), ret_use_heap);
2709+
ffi_call(&cif, addr, ret, arg_values);
27082710

27092711
for (n = 0; n < arg_count; n++) {
27102712
if (arg_types[n]->type == FFI_TYPE_STRUCT) {
@@ -2720,7 +2722,8 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */
27202722
free_alloca(arg_values, arg_values_use_heap);
27212723
}
27222724

2723-
zend_ffi_cdata_to_zval(NULL, (void*)&ret, ZEND_FFI_TYPE(type->func.ret_type), BP_VAR_R, return_value, 0, 1);
2725+
zend_ffi_cdata_to_zval(NULL, ret, ZEND_FFI_TYPE(type->func.ret_type), BP_VAR_R, return_value, 0, 1);
2726+
free_alloca(ret, ret_use_heap);
27242727

27252728
exit:
27262729
zend_string_release(EX(func)->common.function_name);

ext/ffi/tests/bug79096.phpt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Bug #79096 (FFI Struct Segfault)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('ffi')) die('skip ffi extension not available');
6+
if (!extension_loaded('zend-test')) die('skip zend-test extension not available');
7+
?>
8+
--FILE--
9+
<?php
10+
$header = <<<HEADER
11+
struct bug79096 {
12+
uint64_t a;
13+
uint64_t b;
14+
};
15+
16+
struct bug79096 bug79096(void);
17+
HEADER;
18+
19+
if (PHP_OS_FAMILY !== 'Windows') {
20+
$ffi = FFI::cdef($header);
21+
} else {
22+
try {
23+
$ffi = FFI::cdef($header, 'php_zend_test.dll');
24+
} catch (FFI\Exception $ex) {
25+
$dll = $dll = 'php7' . (PHP_ZTS ? 'ts' : '') . (PHP_DEBUG ? '_debug' : '') . '.dll';
26+
$ffi = FFI::cdef($header, $dll);
27+
}
28+
}
29+
30+
$struct = $ffi->bug79096();
31+
var_dump($struct);
32+
?>
33+
--EXPECTF--
34+
object(FFI\CData:struct bug79096)#%d (2) {
35+
["a"]=>
36+
int(1)
37+
["b"]=>
38+
int(1)
39+
}

ext/zend_test/php_test.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,11 @@ extern zend_module_entry zend_test_module_entry;
3232
ZEND_TSRMLS_CACHE_EXTERN()
3333
#endif
3434

35+
struct bug79096 {
36+
uint64_t a;
37+
uint64_t b;
38+
};
39+
40+
ZEND_API struct bug79096 bug79096(void);
41+
3542
#endif

ext/zend_test/test.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,3 +319,12 @@ ZEND_TSRMLS_CACHE_DEFINE()
319319
#endif
320320
ZEND_GET_MODULE(zend_test)
321321
#endif
322+
323+
struct bug79096 bug79096(void)
324+
{
325+
struct bug79096 b;
326+
327+
b.a = 1;
328+
b.b = 1;
329+
return b;
330+
}

0 commit comments

Comments
 (0)