Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 49 additions & 2 deletions ext/ffi/ffi.c
Original file line number Diff line number Diff line change
Expand Up @@ -3639,6 +3639,46 @@ void zend_ffi_cleanup_dcl(zend_ffi_dcl *dcl) /* {{{ */
}
/* }}} */

void zend_ffi_borrow_dcl(zend_ffi_dcl *dcl, const zend_ffi_dcl *common_dcl) /* {{{ */
{
*dcl = *common_dcl;
dcl->type = ZEND_FFI_TYPE(common_dcl->type);
}
/* }}} */

static void zend_ffi_adopt_type(zend_ffi_dcl *dcl, zend_ffi_dcl *common_dcl) /* {{{ */
{
zend_ffi_type *common_type = ZEND_FFI_TYPE(common_dcl->type);
zend_ffi_type **ref = &dcl->type;

if (!ZEND_FFI_TYPE_IS_OWNED(common_dcl->type)) {
return;
}
while (ZEND_FFI_TYPE(*ref) != common_type) {
zend_ffi_type *type = ZEND_FFI_TYPE(*ref);

if (!ZEND_FFI_TYPE_IS_OWNED(*ref)) {
return;
}
switch (type->kind) {
case ZEND_FFI_TYPE_POINTER:
ref = &type->pointer.type;
break;
case ZEND_FFI_TYPE_ARRAY:
ref = &type->array.type;
break;
case ZEND_FFI_TYPE_FUNC:
ref = &type->func.ret_type;
break;
default:
return;
}
}
*ref = ZEND_FFI_TYPE_MAKE_OWNED(common_type);
common_dcl->type = common_type;
}
/* }}} */

static void zend_ffi_throw_parser_error(const char *format, ...) /* {{{ */
{
va_list va;
Expand Down Expand Up @@ -6078,6 +6118,7 @@ void zend_ffi_add_enum_val(zend_ffi_dcl *enum_dcl, const char *name, size_t name
}
sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
if (sym) {
zend_ffi_cleanup_dcl(enum_dcl);
zend_ffi_parser_error("Redeclaration of \"%.*s\" at line %d", name_len, name, FFI_G(line));
} else {
sym = pemalloc(sizeof(zend_ffi_symbol), FFI_G(persistent));
Expand Down Expand Up @@ -6141,14 +6182,15 @@ static zend_result zend_ffi_validate_field_type(zend_ffi_type *type, zend_ffi_ty
}
/* }}} */

void zend_ffi_add_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_len, zend_ffi_dcl *field_dcl) /* {{{ */
void zend_ffi_add_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_len, zend_ffi_dcl *field_dcl, zend_ffi_dcl *common_dcl) /* {{{ */
{
zend_ffi_field *field;
zend_ffi_type *struct_type = ZEND_FFI_TYPE(struct_dcl->type);
zend_ffi_type *field_type;

ZEND_ASSERT(struct_type && struct_type->kind == ZEND_FFI_TYPE_STRUCT);
zend_ffi_finalize_type(field_dcl);
zend_ffi_adopt_type(field_dcl, common_dcl);
field_type = ZEND_FFI_TYPE(field_dcl->type);
if (zend_ffi_validate_field_type(field_type, struct_type) == FAILURE) {
zend_ffi_cleanup_dcl(field_dcl);
Expand Down Expand Up @@ -6644,7 +6686,7 @@ void zend_ffi_add_arg(HashTable **args, const char *name, size_t name_len, zend_
}
/* }}} */

void zend_ffi_declare(const char *name, size_t name_len, zend_ffi_dcl *dcl) /* {{{ */
void zend_ffi_declare(const char *name, size_t name_len, zend_ffi_dcl *dcl, zend_ffi_dcl *common_dcl) /* {{{ */
{
zend_ffi_symbol *sym;

Expand Down Expand Up @@ -6683,9 +6725,12 @@ void zend_ffi_declare(const char *name, size_t name_len, zend_ffi_dcl *dcl) /* {
}
}
}
zend_ffi_adopt_type(dcl, common_dcl);
zend_ffi_cleanup_dcl(dcl);
zend_ffi_parser_error("Redeclaration of \"%.*s\" at line %d", name_len, name, FFI_G(line));
} else {
if ((dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_TYPEDEF) {
zend_ffi_adopt_type(dcl, common_dcl);
if (zend_ffi_validate_vla(ZEND_FFI_TYPE(dcl->type)) == FAILURE) {
zend_ffi_cleanup_dcl(dcl);
LONGJMP(FFI_G(bailout), FAILURE);
Expand Down Expand Up @@ -6713,11 +6758,13 @@ void zend_ffi_declare(const char *name, size_t name_len, zend_ffi_dcl *dcl) /* {

type = ZEND_FFI_TYPE(dcl->type);
if (zend_ffi_validate_type(type, (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_EXTERN, 1) == FAILURE) {
zend_ffi_adopt_type(dcl, common_dcl);
zend_ffi_cleanup_dcl(dcl);
LONGJMP(FFI_G(bailout), FAILURE);
}
if ((dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == 0 ||
(dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_EXTERN) {
zend_ffi_adopt_type(dcl, common_dcl);
sym = pemalloc(sizeof(zend_ffi_symbol), FFI_G(persistent));
sym->kind = (type->kind == ZEND_FFI_TYPE_FUNC) ? ZEND_FFI_SYM_FUNC : ZEND_FFI_SYM_VAR;
sym->type = dcl->type;
Expand Down
24 changes: 13 additions & 11 deletions ext/ffi/ffi.g
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ declarations:
{const char *name;}
{size_t name_len;}
{zend_ffi_dcl dcl;}
{dcl = common_dcl;}
{zend_ffi_borrow_dcl(&dcl, &common_dcl);}
declarator(&dcl, &name, &name_len)
(
{zend_ffi_val asm_str;}
Expand All @@ -89,14 +89,15 @@ declarations:
)?
attributes(&dcl)?
initializer?
{zend_ffi_declare(name, name_len, &dcl);}
{zend_ffi_declare(name, name_len, &dcl, &common_dcl);}
( ","
{dcl = common_dcl;}
{zend_ffi_borrow_dcl(&dcl, &common_dcl);}
declarator(&dcl, &name, &name_len)
attributes(&dcl)?
initializer?
{zend_ffi_declare(name, name_len, &dcl);}
{zend_ffi_declare(name, name_len, &dcl, &common_dcl);}
)*
{zend_ffi_cleanup_dcl(&common_dcl);}
|
/* empty */
{if (common_dcl.flags & (ZEND_FFI_DCL_ENUM | ZEND_FFI_DCL_STRUCT | ZEND_FFI_DCL_UNION)) zend_ffi_cleanup_dcl(&common_dcl);}
Expand Down Expand Up @@ -264,21 +265,22 @@ struct_contents(zend_ffi_dcl *dcl):

struct_declaration(zend_ffi_dcl *struct_dcl):
{zend_ffi_dcl common_field_dcl = ZEND_FFI_ATTR_INIT;}
{zend_ffi_dcl base_field_dcl = ZEND_FFI_ATTR_INIT;}
{zend_ffi_dcl field_dcl;}
specifier_qualifier_list(&common_field_dcl)
{base_field_dcl = common_field_dcl;}
( /* empty */
{zend_ffi_add_anonymous_field(struct_dcl, &common_field_dcl);}
| struct_declarator(struct_dcl, &common_field_dcl)
| {zend_ffi_borrow_dcl(&field_dcl, &common_field_dcl);}
struct_declarator(struct_dcl, &field_dcl, &common_field_dcl)
( ","
{zend_ffi_dcl field_dcl = base_field_dcl;}
{zend_ffi_borrow_dcl(&field_dcl, &common_field_dcl);}
attributes(&field_dcl)?
struct_declarator(struct_dcl, &field_dcl)
struct_declarator(struct_dcl, &field_dcl, &common_field_dcl)
)*
{zend_ffi_cleanup_dcl(&common_field_dcl);}
)
;

struct_declarator(zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl):
struct_declarator(zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl, zend_ffi_dcl *common_field_dcl):
{const char *name = NULL;}
{size_t name_len = 0;}
{zend_ffi_val bits;}
Expand All @@ -289,7 +291,7 @@ struct_declarator(zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl):
{zend_ffi_add_bit_field(struct_dcl, name, name_len, field_dcl, &bits);}
| /*empty */
attributes(field_dcl)?
{zend_ffi_add_field(struct_dcl, name, name_len, field_dcl);}
{zend_ffi_add_field(struct_dcl, name, name_len, field_dcl, common_field_dcl);}
)
| ":"
constant_expression(&bits)
Expand Down
26 changes: 14 additions & 12 deletions ext/ffi/ffi_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ static int parse_type_specifier(int sym, zend_ffi_dcl *dcl);
static int parse_struct_or_union_specifier(int sym, zend_ffi_dcl *dcl);
static int parse_struct_contents(int sym, zend_ffi_dcl *dcl);
static int parse_struct_declaration(int sym, zend_ffi_dcl *struct_dcl);
static int parse_struct_declarator(int sym, zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl);
static int parse_struct_declarator(int sym, zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl, zend_ffi_dcl *common_field_dcl);
static int parse_enum_specifier(int sym, zend_ffi_dcl *dcl);
static int parse_enumerator_list(int sym, zend_ffi_dcl *enum_dcl);
static int parse_enumerator(int sym, zend_ffi_dcl *enum_dcl, int64_t *min, int64_t *max, int64_t *last);
Expand Down Expand Up @@ -2079,7 +2079,7 @@ static int parse_declarations(int sym) {
const char *name;
size_t name_len;
zend_ffi_dcl dcl;
dcl = common_dcl;
zend_ffi_borrow_dcl(&dcl, &common_dcl);
sym = parse_declarator(sym, &dcl, &name, &name_len);
if (sym == YY___ASM__) {
zend_ffi_val asm_str;
Expand All @@ -2102,19 +2102,20 @@ static int parse_declarations(int sym) {
if (sym == YY__EQUAL) {
sym = parse_initializer(sym);
}
zend_ffi_declare(name, name_len, &dcl);
zend_ffi_declare(name, name_len, &dcl, &common_dcl);
while (sym == YY__COMMA) {
sym = get_sym();
dcl = common_dcl;
zend_ffi_borrow_dcl(&dcl, &common_dcl);
sym = parse_declarator(sym, &dcl, &name, &name_len);
if (YY_IN_SET(sym, (YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC,YY___CDECL,YY___STDCALL,YY___FASTCALL,YY___THISCALL,YY___VECTORCALL), "\000\000\000\000\000\000\360\017\000\000\000\000\000")) {
sym = parse_attributes(sym, &dcl);
}
if (sym == YY__EQUAL) {
sym = parse_initializer(sym);
}
zend_ffi_declare(name, name_len, &dcl);
zend_ffi_declare(name, name_len, &dcl, &common_dcl);
}
zend_ffi_cleanup_dcl(&common_dcl);
} else if (sym == YY__SEMICOLON) {
if (common_dcl.flags & (ZEND_FFI_DCL_ENUM | ZEND_FFI_DCL_STRUCT | ZEND_FFI_DCL_UNION)) zend_ffi_cleanup_dcl(&common_dcl);
} else {
Expand Down Expand Up @@ -2472,28 +2473,29 @@ static int parse_struct_contents(int sym, zend_ffi_dcl *dcl) {

static int parse_struct_declaration(int sym, zend_ffi_dcl *struct_dcl) {
zend_ffi_dcl common_field_dcl = ZEND_FFI_ATTR_INIT;
zend_ffi_dcl base_field_dcl = ZEND_FFI_ATTR_INIT;
zend_ffi_dcl field_dcl;
sym = parse_specifier_qualifier_list(sym, &common_field_dcl);
base_field_dcl = common_field_dcl;
if (sym == YY__SEMICOLON || sym == YY__RBRACE) {
zend_ffi_add_anonymous_field(struct_dcl, &common_field_dcl);
} else if (sym == YY__STAR || sym == YY_ID || sym == YY__LPAREN || sym == YY__COLON) {
sym = parse_struct_declarator(sym, struct_dcl, &common_field_dcl);
zend_ffi_borrow_dcl(&field_dcl, &common_field_dcl);
sym = parse_struct_declarator(sym, struct_dcl, &field_dcl, &common_field_dcl);
while (sym == YY__COMMA) {
sym = get_sym();
zend_ffi_dcl field_dcl = base_field_dcl;
zend_ffi_borrow_dcl(&field_dcl, &common_field_dcl);
if (YY_IN_SET(sym, (YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC,YY___CDECL,YY___STDCALL,YY___FASTCALL,YY___THISCALL,YY___VECTORCALL), "\000\000\000\000\000\000\360\017\000\000\000\000\000")) {
sym = parse_attributes(sym, &field_dcl);
}
sym = parse_struct_declarator(sym, struct_dcl, &field_dcl);
sym = parse_struct_declarator(sym, struct_dcl, &field_dcl, &common_field_dcl);
}
zend_ffi_cleanup_dcl(&common_field_dcl);
} else {
yy_error_sym("unexpected", sym);
}
return sym;
}

static int parse_struct_declarator(int sym, zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl) {
static int parse_struct_declarator(int sym, zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl, zend_ffi_dcl *common_field_dcl) {
const char *name = NULL;
size_t name_len = 0;
zend_ffi_val bits;
Expand All @@ -2510,7 +2512,7 @@ static int parse_struct_declarator(int sym, zend_ffi_dcl *struct_dcl, zend_ffi_d
if (YY_IN_SET(sym, (YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC,YY___CDECL,YY___STDCALL,YY___FASTCALL,YY___THISCALL,YY___VECTORCALL), "\000\000\000\000\000\000\360\017\000\000\000\000\000")) {
sym = parse_attributes(sym, field_dcl);
}
zend_ffi_add_field(struct_dcl, name, name_len, field_dcl);
zend_ffi_add_field(struct_dcl, name, name_len, field_dcl, common_field_dcl);
} else {
yy_error_sym("unexpected", sym);
}
Expand Down
5 changes: 3 additions & 2 deletions ext/ffi/php_ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ typedef struct _zend_ffi_val {
zend_result zend_ffi_parse_decl(const char *str, size_t len);
zend_result zend_ffi_parse_type(const char *str, size_t len, zend_ffi_dcl *dcl);
void zend_ffi_cleanup_dcl(zend_ffi_dcl *dcl);
void zend_ffi_borrow_dcl(zend_ffi_dcl *dcl, const zend_ffi_dcl *common_dcl);

/* parser callbacks */
void ZEND_NORETURN zend_ffi_parser_error(const char *msg, ...);
Expand All @@ -220,15 +221,15 @@ void zend_ffi_declare_tag(const char *name, size_t name_len, zend_ffi_dcl *dcl,
void zend_ffi_make_enum_type(zend_ffi_dcl *dcl);
void zend_ffi_add_enum_val(zend_ffi_dcl *enum_dcl, const char *name, size_t name_len, zend_ffi_val *val, int64_t *min, int64_t *max, int64_t *last);
void zend_ffi_make_struct_type(zend_ffi_dcl *dcl);
void zend_ffi_add_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_len, zend_ffi_dcl *field_dcl);
void zend_ffi_add_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_len, zend_ffi_dcl *field_dcl, zend_ffi_dcl *common_dcl);
void zend_ffi_add_anonymous_field(zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl);
void zend_ffi_add_bit_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_len, zend_ffi_dcl *field_dcl, zend_ffi_val *bits);
void zend_ffi_adjust_struct_size(zend_ffi_dcl *dcl);
void zend_ffi_make_pointer_type(zend_ffi_dcl *dcl);
void zend_ffi_make_array_type(zend_ffi_dcl *dcl, zend_ffi_val *len);
void zend_ffi_make_func_type(zend_ffi_dcl *dcl, HashTable *args, zend_ffi_dcl *nested_dcl);
void zend_ffi_add_arg(HashTable **args, const char *name, size_t name_len, zend_ffi_dcl *arg_dcl);
void zend_ffi_declare(const char *name, size_t name_len, zend_ffi_dcl *dcl);
void zend_ffi_declare(const char *name, size_t name_len, zend_ffi_dcl *dcl, zend_ffi_dcl *common_dcl);
void zend_ffi_add_attribute(zend_ffi_dcl *dcl, const char *name, size_t name_len);
void zend_ffi_add_attribute_value(zend_ffi_dcl *dcl, const char *name, size_t name_len, int n, zend_ffi_val *val);
void zend_ffi_add_msvc_attribute_value(zend_ffi_dcl *dcl, const char *name, size_t name_len, zend_ffi_val *val);
Expand Down
46 changes: 46 additions & 0 deletions ext/ffi/tests/decl_list_shared_anonymous_type.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
--TEST--
FFI: declarator lists sharing an anonymous struct, union or enum type
--EXTENSIONS--
ffi
--INI--
ffi.enable=1
--FILE--
<?php

$ffi = FFI::cdef(<<<'C'
typedef struct { int a; } T, *P, A[2];
typedef union { int i; char c; } U1, U2;
typedef enum { E_A, E_B } E1, E2;
struct S { struct { int x; } p, q; enum { F_A } e, f; };
static struct { int z; } s1, *s2;
C);

$t = $ffi->new("T");
$t->a = 1;
$a = $ffi->new("A");
$a[1]->a = 2;
$p = $ffi->new("P");
$p = FFI::addr($t);
echo $t->a, $a[1]->a, $p->a, PHP_EOL;

$u = $ffi->new("U2");
$u->i = 3;
echo $u->i, PHP_EOL;

echo FFI::sizeof($ffi->new("E1")), FFI::sizeof($ffi->new("E2")), PHP_EOL;

$s = $ffi->new("struct S");
$s->p->x = 4;
$s->q->x = 5;
echo $s->p->x, $s->q->x, PHP_EOL;

unset($ffi, $t, $a, $p, $u, $s);
echo "Done", PHP_EOL;

?>
--EXPECT--
121
3
44
45
Done
36 changes: 36 additions & 0 deletions ext/ffi/tests/redeclaration_leak.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
FFI: no memory leak on redeclaration errors
--EXTENSIONS--
ffi
--INI--
ffi.enable=1
--FILE--
<?php

$cases = [
'int x; int *x;',
'int x; struct { int a; } *x;',
'typedef struct { int a; } T; typedef struct { int a; } T;',
'typedef struct { int a; } T, U; typedef struct { int a; } *U;',
'enum { A }; enum { A };',
'int A; enum { A };',
'void f(enum { B, B } e);',
];

foreach ($cases as $code) {
try {
FFI::cdef($code);
} catch (FFI\ParserException $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
}

?>
--EXPECT--
FFI\ParserException: Redeclaration of "x" at line 1
FFI\ParserException: Redeclaration of "x" at line 1
FFI\ParserException: Redeclaration of "T" at line 1
FFI\ParserException: Redeclaration of "U" at line 1
FFI\ParserException: Redeclaration of "A" at line 1
FFI\ParserException: Redeclaration of "A" at line 1
FFI\ParserException: Redeclaration of "B" at line 1
Loading