diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 73e738202c8d..c0401aef0b21 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -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; @@ -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)); @@ -6141,7 +6182,7 @@ 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); @@ -6149,6 +6190,7 @@ void zend_ffi_add_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_ 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); @@ -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; @@ -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); @@ -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; diff --git a/ext/ffi/ffi.g b/ext/ffi/ffi.g index 8102e8f73a87..bed38ef00842 100644 --- a/ext/ffi/ffi.g +++ b/ext/ffi/ffi.g @@ -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;} @@ -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);} @@ -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;} @@ -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) diff --git a/ext/ffi/ffi_parser.c b/ext/ffi/ffi_parser.c index bda13d323eda..7084faa34aa8 100644 --- a/ext/ffi/ffi_parser.c +++ b/ext/ffi/ffi_parser.c @@ -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); @@ -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; @@ -2102,10 +2102,10 @@ 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); @@ -2113,8 +2113,9 @@ 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); } + 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 { @@ -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; @@ -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); } diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 69ee099528f5..02962269db68 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -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, ...); @@ -220,7 +221,7 @@ 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); @@ -228,7 +229,7 @@ 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); diff --git a/ext/ffi/tests/decl_list_shared_anonymous_type.phpt b/ext/ffi/tests/decl_list_shared_anonymous_type.phpt new file mode 100644 index 000000000000..60d7cc59eb32 --- /dev/null +++ b/ext/ffi/tests/decl_list_shared_anonymous_type.phpt @@ -0,0 +1,46 @@ +--TEST-- +FFI: declarator lists sharing an anonymous struct, union or enum type +--EXTENSIONS-- +ffi +--INI-- +ffi.enable=1 +--FILE-- +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 diff --git a/ext/ffi/tests/redeclaration_leak.phpt b/ext/ffi/tests/redeclaration_leak.phpt new file mode 100644 index 000000000000..3454c2bbd8f1 --- /dev/null +++ b/ext/ffi/tests/redeclaration_leak.phpt @@ -0,0 +1,36 @@ +--TEST-- +FFI: no memory leak on redeclaration errors +--EXTENSIONS-- +ffi +--INI-- +ffi.enable=1 +--FILE-- +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