Skip to content

Commit d530087

Browse files
committed
Fix #79571: FFI: var_dumping unions may segfault
We must not attempt to access arbitrary union members when retrieving debug info, because that may not be valid. Therefore we do no longer dereference pointer types inside of unions, but report their address as string in `%p` format instead.
1 parent d050d74 commit d530087

File tree

3 files changed

+46
-12
lines changed

3 files changed

+46
-12
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ PHP NEWS
55
- Core:
66
. Fixed bug #79566 (Private SHM is not private on Windows). (cmb)
77

8+
- FFI:
9+
. Fixed bug #79571 (FFI: var_dumping unions may segfault). (cmb)
10+
811
- SimpleXML:
912
. Fixed bug #79528 (Different object of the same xml between 7.4.5 and
1013
7.4.4). (cmb)

ext/ffi/ffi.c

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow_ret(void *p
459459
}
460460
/* }}} */
461461

462-
static zend_always_inline void zend_ffi_cdata_to_zval(zend_ffi_cdata *cdata, void *ptr, zend_ffi_type *type, int read_type, zval *rv, zend_ffi_flags flags, zend_bool is_ret) /* {{{ */
462+
static zend_always_inline void zend_ffi_cdata_to_zval(zend_ffi_cdata *cdata, void *ptr, zend_ffi_type *type, int read_type, zval *rv, zend_ffi_flags flags, zend_bool is_ret, zend_bool debug_union) /* {{{ */
463463
{
464464
if (read_type == BP_VAR_R) {
465465
zend_ffi_type_kind kind = type->kind;
@@ -514,6 +514,9 @@ static zend_always_inline void zend_ffi_cdata_to_zval(zend_ffi_cdata *cdata, voi
514514
if (*(void**)ptr == NULL) {
515515
ZVAL_NULL(rv);
516516
return;
517+
} else if (debug_union) {
518+
ZVAL_STR(rv, zend_strpprintf(0, "%p", *(void**)ptr));
519+
return;
517520
} else if ((type->attr & ZEND_FFI_ATTR_CONST) && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) {
518521
ZVAL_STRING(rv, *(char**)ptr);
519522
return;
@@ -864,7 +867,7 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v
864867

865868
ZEND_HASH_FOREACH_PTR(callback_data->type->func.args, arg_type) {
866869
arg_type = ZEND_FFI_TYPE(arg_type);
867-
zend_ffi_cdata_to_zval(NULL, args[n], arg_type, BP_VAR_R, &fci.params[n], (zend_ffi_flags)(arg_type->attr & ZEND_FFI_ATTR_CONST), 0);
870+
zend_ffi_cdata_to_zval(NULL, args[n], arg_type, BP_VAR_R, &fci.params[n], (zend_ffi_flags)(arg_type->attr & ZEND_FFI_ATTR_CONST), 0, 0);
868871
n++;
869872
} ZEND_HASH_FOREACH_END();
870873
}
@@ -999,7 +1002,7 @@ static zval *zend_ffi_cdata_get(zval *object, zval *member, int read_type, void
9991002
return &EG(uninitialized_zval);;
10001003
}
10011004

1002-
zend_ffi_cdata_to_zval(cdata, cdata->ptr, type, BP_VAR_R, rv, 0, 0);
1005+
zend_ffi_cdata_to_zval(cdata, cdata->ptr, type, BP_VAR_R, rv, 0, 0, 0);
10031006
return rv;
10041007
}
10051008
/* }}} */
@@ -1171,7 +1174,7 @@ static zval *zend_ffi_cdata_read_field(zval *object, zval *member, int read_type
11711174
}
11721175
}
11731176
ptr = (void*)(((char*)ptr) + field->offset);
1174-
zend_ffi_cdata_to_zval(NULL, ptr, field_type, read_type, rv, (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)field->is_const, 0);
1177+
zend_ffi_cdata_to_zval(NULL, ptr, field_type, read_type, rv, (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)field->is_const, 0, 0);
11751178
} else {
11761179
zend_ffi_bit_field_to_zval(ptr, field, rv);
11771180
}
@@ -1312,7 +1315,7 @@ static zval *zend_ffi_cdata_read_dim(zval *object, zval *offset, int read_type,
13121315
return &EG(uninitialized_zval);
13131316
}
13141317

1315-
zend_ffi_cdata_to_zval(NULL, ptr, dim_type, read_type, rv, is_const, 0);
1318+
zend_ffi_cdata_to_zval(NULL, ptr, dim_type, read_type, rv, is_const, 0, 0);
13161319
return rv;
13171320
}
13181321
/* }}} */
@@ -1863,7 +1866,7 @@ static zval *zend_ffi_cdata_it_get_current_data(zend_object_iterator *it) /* {{{
18631866
ptr = (void*)((char*)cdata->ptr + dim_type->size * iter->it.index);
18641867

18651868
zval_ptr_dtor(&iter->value);
1866-
zend_ffi_cdata_to_zval(NULL, ptr, dim_type, iter->by_ref ? BP_VAR_RW : BP_VAR_R, &iter->value, (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST), 0);
1869+
zend_ffi_cdata_to_zval(NULL, ptr, dim_type, iter->by_ref ? BP_VAR_RW : BP_VAR_R, &iter->value, (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST), 0, 0);
18671870
return &iter->value;
18681871
}
18691872
/* }}} */
@@ -1958,7 +1961,7 @@ static HashTable *zend_ffi_cdata_get_debug_info(zval *object, int *is_temp) /* {
19581961
case ZEND_FFI_TYPE_SINT32:
19591962
case ZEND_FFI_TYPE_UINT64:
19601963
case ZEND_FFI_TYPE_SINT64:
1961-
zend_ffi_cdata_to_zval(cdata, ptr, type, BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0);
1964+
zend_ffi_cdata_to_zval(cdata, ptr, type, BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0, 0);
19621965
ht = zend_new_array(1);
19631966
zend_hash_str_add(ht, "cdata", sizeof("cdata")-1, &tmp);
19641967
*is_temp = 1;
@@ -1978,7 +1981,7 @@ static HashTable *zend_ffi_cdata_get_debug_info(zval *object, int *is_temp) /* {
19781981
*is_temp = 1;
19791982
return ht;
19801983
} else {
1981-
zend_ffi_cdata_to_zval(NULL, *(void**)ptr, ZEND_FFI_TYPE(type->pointer.type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0);
1984+
zend_ffi_cdata_to_zval(NULL, *(void**)ptr, ZEND_FFI_TYPE(type->pointer.type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0, 0);
19821985
ht = zend_new_array(1);
19831986
zend_hash_index_add_new(ht, 0, &tmp);
19841987
*is_temp = 1;
@@ -1991,7 +1994,7 @@ static HashTable *zend_ffi_cdata_get_debug_info(zval *object, int *is_temp) /* {
19911994
if (key) {
19921995
if (!f->bits) {
19931996
void *f_ptr = (void*)(((char*)ptr) + f->offset);
1994-
zend_ffi_cdata_to_zval(NULL, f_ptr, ZEND_FFI_TYPE(f->type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0);
1997+
zend_ffi_cdata_to_zval(NULL, f_ptr, ZEND_FFI_TYPE(f->type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0, type->attr & ZEND_FFI_ATTR_UNION);
19951998
zend_hash_add(ht, key, &tmp);
19961999
} else {
19972000
zend_ffi_bit_field_to_zval(ptr, f, &tmp);
@@ -2004,7 +2007,7 @@ static HashTable *zend_ffi_cdata_get_debug_info(zval *object, int *is_temp) /* {
20042007
case ZEND_FFI_TYPE_ARRAY:
20052008
ht = zend_new_array(type->array.length);
20062009
for (n = 0; n < type->array.length; n++) {
2007-
zend_ffi_cdata_to_zval(NULL, ptr, ZEND_FFI_TYPE(type->array.type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0);
2010+
zend_ffi_cdata_to_zval(NULL, ptr, ZEND_FFI_TYPE(type->array.type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0, 0);
20082011
zend_hash_index_add(ht, n, &tmp);
20092012
ptr = (void*)(((char*)ptr) + ZEND_FFI_TYPE(type->array.type)->size);
20102013
}
@@ -2373,7 +2376,7 @@ static zval *zend_ffi_read_var(zval *object, zval *member, int read_type, void *
23732376
zend_tmp_string_release(tmp_var_name);
23742377

23752378
if (sym->kind == ZEND_FFI_SYM_VAR) {
2376-
zend_ffi_cdata_to_zval(NULL, sym->addr, ZEND_FFI_TYPE(sym->type), read_type, rv, (zend_ffi_flags)sym->is_const, 0);
2379+
zend_ffi_cdata_to_zval(NULL, sym->addr, ZEND_FFI_TYPE(sym->type), read_type, rv, (zend_ffi_flags)sym->is_const, 0, 0);
23772380
} else if (sym->kind == ZEND_FFI_SYM_FUNC) {
23782381
zend_ffi_cdata *cdata;
23792382
zend_ffi_type *new_type = emalloc(sizeof(zend_ffi_type));
@@ -2746,7 +2749,7 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */
27462749
free_alloca(arg_values, arg_values_use_heap);
27472750
}
27482751

2749-
zend_ffi_cdata_to_zval(NULL, ret, ZEND_FFI_TYPE(type->func.ret_type), BP_VAR_R, return_value, 0, 1);
2752+
zend_ffi_cdata_to_zval(NULL, ret, ZEND_FFI_TYPE(type->func.ret_type), BP_VAR_R, return_value, 0, 1, 0);
27502753
free_alloca(ret, ret_use_heap);
27512754

27522755
exit:

ext/ffi/tests/bug79571.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Bug #79571 (FFI: var_dumping unions may segfault)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('ffi')) die('skip ffi extension not available');
6+
?>
7+
--FILE--
8+
<?php
9+
$ffi = FFI::cdef(<<<EOF
10+
typedef union {
11+
int num;
12+
char *str;
13+
} my_union;
14+
EOF);
15+
16+
$union = $ffi->new('my_union');
17+
$union->num = 42;
18+
var_dump($union);
19+
var_dump($union->num);
20+
?>
21+
--EXPECTF--
22+
object(FFI\CData:union <anonymous>)#%d (2) {
23+
["num"]=>
24+
int(42)
25+
["str"]=>
26+
string(4) "0x2a"
27+
}
28+
int(42)

0 commit comments

Comments
 (0)