Skip to content

Commit

Permalink
Reset EG(trampoline).op_array.last_var that FFI may modify
Browse files Browse the repository at this point in the history
Closes GH-10916
  • Loading branch information
iluuu1994 committed Mar 27, 2023
1 parent 21e0305 commit 4e0bd03
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 0 deletions.
1 change: 1 addition & 0 deletions NEWS
Expand Up @@ -15,6 +15,7 @@ PHP NEWS
(nielsdos)
. Fixed bug GH-10810 (Fix NUL byte terminating Exception::__toString()).
(ilutov)
. Fix potential memory corruption when mixing __callStatic() and FFI. (ilutov)

- Date:
. Fixed bug GH-10583 (DateTime modify with tz pattern should not update
Expand Down
6 changes: 6 additions & 0 deletions Zend/zend_object_handlers.c
Expand Up @@ -1253,6 +1253,12 @@ ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend
ZEND_MAP_PTR_INIT(func->run_time_cache, (void***)&dummy);
func->scope = fbc->common.scope;
/* reserve space for arguments, local and temporary variables */
/* EG(trampoline) is reused from other places, like FFI (e.g. zend_ffi_cdata_get_closure()) where
* it is used as an internal function. It may set fields that don't belong to common, thus
* modifying zend_op_array specific data, most significantly last_var. We need to reset this
* value so that it doesn't contain garbage when the engine allocates space for the next stack
* frame. This didn't cause any issues until now due to "lucky" structure layout. */
func->last_var = 0;
func->T = (fbc->type == ZEND_USER_FUNCTION)? MAX(fbc->op_array.last_var + fbc->op_array.T, 2) : 2;
func->filename = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.filename : ZSTR_EMPTY_ALLOC();
func->line_start = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_start : 0;
Expand Down
43 changes: 43 additions & 0 deletions ext/ffi/tests/trampoline_reset.phpt
@@ -0,0 +1,43 @@
--TEST--
Memory corruption when mixing __callStatic() and FFI
--EXTENSIONS--
ffi
--SKIPIF--
<?php
try {
$libc = FFI::cdef("int printf(const char *format, ...);", "libc.so.6");
} catch (Throwable $_) {
die('skip libc.so.6 not available');
}
?>
--INI--
ffi.enable=1
--FILE--
<?php
class Test
{
public static function __callStatic($name, $args)
{
echo "$name called\n";
}
}

$header = '
typedef struct _IO_FILE FILE;
extern FILE *stdout;
int fprintf(FILE *, const char *, ...);
int fflush(FILE *);
';
$ffi = FFI::cdef($header, 'libc.so.6');

Test::foo();
Test::bar();
$ffi->fprintf($ffi->stdout, "FFI\n");
$ffi->fflush($ffi->stdout);
Test::baz();
?>
--EXPECT--
foo called
bar called
FFI
baz called

0 comments on commit 4e0bd03

Please sign in to comment.