Skip to content

Crash in ZEND_RETURN/GC/zend_call_function #9323

Closed
@tstarling

Description

@tstarling

Description

I hit a segfault while running MediaWiki tests. I have not yet figured out how to make a short test case, but I was able to isolate the bug using gdb.

(gdb) bt
#0  0x000055555580cc23 in zend_call_function (fci=0x7fffffff9fa0, fci_cache=0x7fffffff9f80) at ./Zend/zend_execute_API.c:693
#1  0x000055555580d87d in zend_call_known_function (fn=fn@entry=0x7ffff14614b8, object=object@entry=0x7fffd16e6140, 
    called_scope=<optimized out>, retval_ptr=retval_ptr@entry=0x0, param_count=param_count@entry=0, params=params@entry=0x0, named_params=0x0)
    at ./Zend/zend_execute_API.c:985
#2  0x00005555558a08f4 in zend_call_known_instance_method (params=0x0, param_count=0, retval_ptr=0x0, object=0x7fffd16e6140, fn=0x7ffff14614b8)
    at ./Zend/zend_API.h:596
#3  zend_call_known_instance_method_with_0_params (retval_ptr=0x0, object=0x7fffd16e6140, fn=0x7ffff14614b8) at ./Zend/zend_API.h:596
#4  zend_objects_destroy_object (object=0x7fffd16e6140) at ./Zend/zend_objects.c:163
#5  0x0000555555893d90 in zend_gc_collect_cycles () at ./Zend/zend_gc.c:1549
#6  0x0000555555892b47 in gc_possible_root_when_full (ref=0x7fffd19cd2a0) at ./Zend/zend_gc.c:592
#7  0x000055555588341b in execute_ex (ex=0x7fffffff9fa0) at ./Zend/zend_vm_execute.h:58430
#8  0x000055555588660d in zend_execute (op_array=0x7ffff5489000, return_value=0x0) at ./Zend/zend_vm_execute.h:59499
#9  0x000055555581b0bd in zend_execute_scripts (type=-180232560, type@entry=8, retval=retval@entry=0x0, file_count=file_count@entry=3)
    at ./Zend/zend.c:1694
#10 0x00005555557b8a8a in php_execute_script (primary_file=<optimized out>) at ./main/main.c:2542
#11 0x00005555558acd18 in do_cli (argc=3, argv=0x555555a43410) at ./sapi/cli/php_cli.c:951
#12 0x000055555566c8b8 in main (argc=3, argv=0x555555a43410) at ./sapi/cli/php_cli.c:1339

The code at frame 7 is:

if (EXPECTED(!(EX_CALL_INFO() & (ZEND_CALL_CODE|ZEND_CALL_OBSERVED)))) {
	zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
	ZVAL_COPY_VALUE(return_value, retval_ptr);
	if (GC_MAY_LEAK(ref)) {
		gc_possible_root(ref);
	}
	ZVAL_NULL(retval_ptr);
	break;

It fails to call SAVE_OPLINE() before invoking the GC. At frame 0 we have

} else if (EG(current_execute_data)->func &&
		   ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
		   EG(current_execute_data)->opline->opcode != ZEND_DO_FCALL &&
		   EG(current_execute_data)->opline->opcode != ZEND_DO_ICALL &&
		   EG(current_execute_data)->opline->opcode != ZEND_DO_UCALL &&
		   EG(current_execute_data)->opline->opcode != ZEND_DO_FCALL_BY_NAME) {

but EG(current_execute_data)->opline is uninitialised garbage, so dereferencing it segfaults.

PHP Version

PHP 8.0.20

Operating System

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions