mirror of
https://github.com/php/php-src.git
synced 2026-10-28 09:46:46 +08:00
Merge branch 'PHP-8.6'
* PHP-8.6: Fix GH-24582: Memory leaks in ext/ffi parser when finalize_type() bails out
This commit is contained in:
5 files changed
+115
-13
No files matched your search
+26
-13
@@ -3675,6 +3675,11 @@ static void zend_ffi_throw_parser_error(const char *format, ...) /* {{{ */
|
||||
va_list va;
|
||||
char *message = NULL;
|
||||
|
||||
if (FFI_G(parse_failed)) {
|
||||
/* An error was already reported, parsing only continues to unwind cleanly */
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(va, format);
|
||||
zend_vspprintf(&message, 0, format, va);
|
||||
|
||||
@@ -5750,21 +5755,23 @@ ZEND_GET_MODULE(ffi)
|
||||
/* parser callbacks */
|
||||
void zend_ffi_parser_error(const char *format, ...) /* {{{ */
|
||||
{
|
||||
va_list va;
|
||||
char *message = NULL;
|
||||
if (!FFI_G(parse_failed)) {
|
||||
va_list va;
|
||||
char *message = NULL;
|
||||
|
||||
va_start(va, format);
|
||||
zend_vspprintf(&message, 0, format, va);
|
||||
va_start(va, format);
|
||||
zend_vspprintf(&message, 0, format, va);
|
||||
|
||||
if (EG(current_execute_data)) {
|
||||
zend_throw_exception(zend_ffi_parser_exception_ce, message, 0);
|
||||
} else {
|
||||
zend_error(E_WARNING, "FFI Parser: %s", message);
|
||||
if (EG(current_execute_data)) {
|
||||
zend_throw_exception(zend_ffi_parser_exception_ce, message, 0);
|
||||
} else {
|
||||
zend_error(E_WARNING, "FFI Parser: %s", message);
|
||||
}
|
||||
|
||||
efree(message);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
efree(message);
|
||||
va_end(va);
|
||||
|
||||
LONGJMP(FFI_G(bailout), FAILURE);
|
||||
}
|
||||
/* }}} */
|
||||
@@ -5849,10 +5856,16 @@ static void zend_ffi_finalize_type(zend_ffi_dcl *dcl) /* {{{ */
|
||||
case ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_COMPLEX:
|
||||
case ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_COMPLEX:
|
||||
case ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_COMPLEX:
|
||||
zend_ffi_parser_error("Unsupported type _Complex at line %d", FFI_G(line));
|
||||
zend_ffi_throw_parser_error("Unsupported type _Complex at line %d", FFI_G(line));
|
||||
/* Do not bail out here: callers may hold memory that is not owned by dcl yet.
|
||||
* Continue with a placeholder type, zend_ffi_parse_*() report the failure. */
|
||||
FFI_G(parse_failed) = 1;
|
||||
dcl->type = (zend_ffi_type*)&zend_ffi_type_sint32;
|
||||
break;
|
||||
default:
|
||||
zend_ffi_parser_error("Unsupported type specifier combination at line %d", FFI_G(line));
|
||||
zend_ffi_throw_parser_error("Unsupported type specifier combination at line %d", FFI_G(line));
|
||||
FFI_G(parse_failed) = 1;
|
||||
dcl->type = (zend_ffi_type*)&zend_ffi_type_sint32;
|
||||
break;
|
||||
}
|
||||
dcl->flags &= ~ZEND_FFI_DCL_TYPE_SPECIFIERS;
|
||||
|
||||
@@ -886,9 +886,15 @@ zend_result zend_ffi_parse_decl(const char *str, size_t len) {
|
||||
FFI_G(attribute_parsing) = 0;
|
||||
yy_buf = (unsigned char*)str;
|
||||
yy_end = yy_buf + len;
|
||||
FFI_G(parse_failed) = 0;
|
||||
parse();
|
||||
if (FFI_G(parse_failed)) {
|
||||
FFI_G(parse_failed) = 0;
|
||||
return FAILURE;
|
||||
}
|
||||
return SUCCESS;
|
||||
} else {
|
||||
FFI_G(parse_failed) = 0;
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
@@ -902,13 +908,19 @@ zend_result zend_ffi_parse_type(const char *str, size_t len, zend_ffi_dcl *dcl)
|
||||
yy_pos = yy_text = yy_buf = (unsigned char*)str;
|
||||
yy_end = yy_buf + len;
|
||||
yy_line = 1;
|
||||
FFI_G(parse_failed) = 0;
|
||||
sym = parse_type_name(get_sym(), dcl);
|
||||
if (sym != YY_EOF) {
|
||||
yy_error_sym("<EOF> expected, got", sym);
|
||||
}
|
||||
zend_ffi_validate_type_name(dcl);
|
||||
if (FFI_G(parse_failed)) {
|
||||
FFI_G(parse_failed) = 0;
|
||||
return FAILURE;
|
||||
}
|
||||
return SUCCESS;
|
||||
} else {
|
||||
FFI_G(parse_failed) = 0;
|
||||
return FAILURE;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3621,9 +3621,15 @@ zend_result zend_ffi_parse_decl(const char *str, size_t len) {
|
||||
FFI_G(attribute_parsing) = 0;
|
||||
yy_buf = (unsigned char*)str;
|
||||
yy_end = yy_buf + len;
|
||||
FFI_G(parse_failed) = 0;
|
||||
parse();
|
||||
if (FFI_G(parse_failed)) {
|
||||
FFI_G(parse_failed) = 0;
|
||||
return FAILURE;
|
||||
}
|
||||
return SUCCESS;
|
||||
} else {
|
||||
FFI_G(parse_failed) = 0;
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
@@ -3637,13 +3643,19 @@ zend_result zend_ffi_parse_type(const char *str, size_t len, zend_ffi_dcl *dcl)
|
||||
yy_pos = yy_text = yy_buf = (unsigned char*)str;
|
||||
yy_end = yy_buf + len;
|
||||
yy_line = 1;
|
||||
FFI_G(parse_failed) = 0;
|
||||
sym = parse_type_name(get_sym(), dcl);
|
||||
if (sym != YY_EOF) {
|
||||
yy_error_sym("<EOF> expected, got", sym);
|
||||
}
|
||||
zend_ffi_validate_type_name(dcl);
|
||||
if (FFI_G(parse_failed)) {
|
||||
FFI_G(parse_failed) = 0;
|
||||
return FAILURE;
|
||||
}
|
||||
return SUCCESS;
|
||||
} else {
|
||||
FFI_G(parse_failed) = 0;
|
||||
return FAILURE;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ ZEND_BEGIN_MODULE_GLOBALS(ffi)
|
||||
bool allow_vla;
|
||||
bool attribute_parsing;
|
||||
bool persistent;
|
||||
bool parse_failed;
|
||||
uint32_t default_type_attr;
|
||||
ZEND_END_MODULE_GLOBALS(ffi)
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
--TEST--
|
||||
FFI: no memory leak when zend_ffi_finalize_type() bails out inside a declarator
|
||||
--EXTENSIONS--
|
||||
ffi
|
||||
--INI--
|
||||
ffi.enable=1
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function leaks(string $code): bool {
|
||||
$n = 200;
|
||||
for ($i = 0; $i < 20; $i++) {
|
||||
try { FFI::cdef($code); } catch (FFI\ParserException) {}
|
||||
}
|
||||
$before = memory_get_usage();
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
try { FFI::cdef($code); } catch (FFI\ParserException) {}
|
||||
}
|
||||
/* A leaked block is at least 16 bytes, so this catches a leak per call */
|
||||
return memory_get_usage() - $before >= $n;
|
||||
}
|
||||
|
||||
$cases = [
|
||||
// zend_ffi_make_pointer_type()
|
||||
'double _Complex *x;',
|
||||
'double _Complex **x;',
|
||||
// zend_ffi_make_func_type()
|
||||
'double _Complex f(int a, int b);',
|
||||
// zend_ffi_add_arg(): first and later argument
|
||||
'void f(double _Complex x);',
|
||||
'void f(int a, double _Complex x);',
|
||||
// zend_ffi_nested_declaration()
|
||||
'double _Complex (x[3]);',
|
||||
'double _Complex (f(int a));',
|
||||
// parsing continues after the error: only the first one is reported
|
||||
'double _Complex x; int y;',
|
||||
'struct S { double _Complex a; int b; };',
|
||||
'int x; double _Complex y; double _Complex z;',
|
||||
];
|
||||
|
||||
foreach ($cases as $code) {
|
||||
try {
|
||||
FFI::cdef($code);
|
||||
echo "no error: $code\n";
|
||||
} catch (FFI\ParserException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
if (leaks($code)) {
|
||||
echo "leak: $code\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Unsupported type _Complex at line 1
|
||||
Unsupported type _Complex at line 1
|
||||
Unsupported type _Complex at line 1
|
||||
Unsupported type _Complex at line 1
|
||||
Unsupported type _Complex at line 1
|
||||
Unsupported type _Complex at line 1
|
||||
Unsupported type _Complex at line 1
|
||||
Unsupported type _Complex at line 1
|
||||
Unsupported type _Complex at line 1
|
||||
Unsupported type _Complex at line 1
|
||||
Reference in new issue
Block a user