Fix handling of abstract/deprecated exception
authorNikita Popov <nikita.ppv@gmail.com>
Wed, 4 Sep 2019 10:34:20 +0000 (12:34 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 4 Sep 2019 10:46:00 +0000 (12:46 +0200)
The exception mechanism assumes that exceptions from DO_FCALL are
already happening after the function call. This means that we are
currently leaking the passed arguments, and I think we can also
corrupt the VM stack due to incorrect frame linking in some cases
(there are assertion failures if the VM stack page size is reduced).

Instead handle the stack frame freeing manually for this special
case.

Zend/tests/call_to_abstract_method_args.phpt [new file with mode: 0644]
Zend/zend_execute.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/Zend/tests/call_to_abstract_method_args.phpt b/Zend/tests/call_to_abstract_method_args.phpt
new file mode 100644 (file)
index 0000000..4f7f09e
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+Check that arguments are freed when a call to an abstract method throws
+--FILE--
+<?php
+
+abstract class Test {
+    abstract static function method();
+}
+
+try {
+    Test::method(new stdClass);
+} catch (Error $e) {
+    echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+Cannot call abstract method Test::method()
index 556a8f8493a8ddba1dadc570d29171810ecf621c..c831ef7ec745bafb9e6c5f045fc1e51fdb60cfbf 100644 (file)
@@ -1537,6 +1537,12 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(c
                ZSTR_VAL(fbc->common.function_name));
 }
 
+static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_abstract_method(const zend_function *fbc)
+{
+       zend_throw_error(NULL, "Cannot call abstract method %s::%s()",
+               ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+}
+
 static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value OPLINE_DC EXECUTE_DATA_DC)
 {
        zend_uchar c;
index aa7a80c95c071daf11bcd1da1fe40371608b0234..1dec6a0a3fad599a306bef6599ebaece37877014 100644 (file)
@@ -1047,16 +1047,6 @@ ZEND_VM_COLD_HELPER(zend_this_not_in_object_context_helper, ANY, ANY)
        HANDLE_EXCEPTION();
 }
 
-ZEND_VM_COLD_HELPER(zend_abstract_method_helper, ANY, ANY, zend_function *func)
-{
-       USE_OPLINE
-
-       SAVE_OPLINE();
-       zend_throw_error(NULL, "Cannot call abstract method %s::%s()", ZSTR_VAL(func->common.scope->name), ZSTR_VAL(func->common.function_name));
-       UNDEF_RESULT();
-       HANDLE_EXCEPTION();
-}
-
 ZEND_VM_COLD_HELPER(zend_undefined_function_helper, ANY, ANY)
 {
        USE_OPLINE
@@ -4143,12 +4133,15 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL))
        EX(call) = call->prev_execute_data;
        if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) {
                if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) {
-                       ZEND_VM_DISPATCH_TO_HELPER(zend_abstract_method_helper, func, fbc);
+                       zend_abstract_method(fbc);
+ZEND_VM_C_LABEL(fcall_except):
+                       UNDEF_RESULT();
+                       zend_vm_stack_free_args(call);
+                       ZEND_VM_C_GOTO(fcall_end);
                } else {
                        zend_deprecated_function(fbc);
                        if (UNEXPECTED(EG(exception) != NULL)) {
-                               UNDEF_RESULT();
-                               HANDLE_EXCEPTION();
+                               ZEND_VM_C_GOTO(fcall_except);
                        }
                }
        }
index 6bc3bb2e9452733e8e69abf953625f2408e8324a..3af0f5a0efe1331c435b2684e91975dab99becbc 100644 (file)
@@ -781,16 +781,6 @@ static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_th
        HANDLE_EXCEPTION();
 }
 
-static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_abstract_method_helper_SPEC(zend_function *func ZEND_OPCODE_HANDLER_ARGS_DC)
-{
-       USE_OPLINE
-
-       SAVE_OPLINE();
-       zend_throw_error(NULL, "Cannot call abstract method %s::%s()", ZSTR_VAL(func->common.scope->name), ZSTR_VAL(func->common.function_name));
-       UNDEF_RESULT();
-       HANDLE_EXCEPTION();
-}
-
 static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_undefined_function_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS)
 {
        USE_OPLINE
@@ -1557,12 +1547,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
        EX(call) = call->prev_execute_data;
        if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) {
                if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) {
-                       ZEND_VM_TAIL_CALL(zend_abstract_method_helper_SPEC(fbc ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+                       zend_abstract_method(fbc);
+fcall_except:
+                       UNDEF_RESULT();
+                       zend_vm_stack_free_args(call);
+                       goto fcall_end;
                } else {
                        zend_deprecated_function(fbc);
                        if (UNEXPECTED(EG(exception) != NULL)) {
-                               UNDEF_RESULT();
-                               HANDLE_EXCEPTION();
+                               goto fcall_except;
                        }
                }
        }
@@ -1668,12 +1661,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
        EX(call) = call->prev_execute_data;
        if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) {
                if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) {
-                       ZEND_VM_TAIL_CALL(zend_abstract_method_helper_SPEC(fbc ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+                       zend_abstract_method(fbc);
+fcall_except:
+                       UNDEF_RESULT();
+                       zend_vm_stack_free_args(call);
+                       goto fcall_end;
                } else {
                        zend_deprecated_function(fbc);
                        if (UNEXPECTED(EG(exception) != NULL)) {
-                               UNDEF_RESULT();
-                               HANDLE_EXCEPTION();
+                               goto fcall_except;
                        }
                }
        }