Skip to content

Commit 01d8454

Browse files
committed
Verify internal types before abandoning call frame
An internal caller executing a builtin method with a static return type will lose context if we drop our frame before performing the validation.
1 parent 7d65cc8 commit 01d8454

File tree

6 files changed

+84
-6
lines changed

6 files changed

+84
-6
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ PHP NEWS
55
- Core:
66
. Fixed bug GH-8338 (Intel CET is disabled unintentionally). (Chen, Hu)
77
. Fixed leak in Enum::from/tryFrom for internal enums when using JIT (ilutov)
8+
. Fixed calling internal methods with a static return type from
9+
extension code. (Sara)
810

911
- Date:
1012
. Fixed bug #72963 (Null-byte injection in CreateFromFormat and related

Zend/zend_execute_API.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -921,11 +921,6 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
921921
} else {
922922
zend_execute_internal(call, fci->retval);
923923
}
924-
EG(current_execute_data) = call->prev_execute_data;
925-
zend_vm_stack_free_args(call);
926-
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
927-
zend_array_release(call->extra_named_params);
928-
}
929924

930925
#if ZEND_DEBUG
931926
if (!EG(exception) && call->func) {
@@ -938,6 +933,11 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
938933
? Z_ISREF_P(fci->retval) : !Z_ISREF_P(fci->retval));
939934
}
940935
#endif
936+
EG(current_execute_data) = call->prev_execute_data;
937+
zend_vm_stack_free_args(call);
938+
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
939+
zend_array_release(call->extra_named_params);
940+
}
941941

942942
if (EG(exception)) {
943943
zval_ptr_dtor(fci->retval);

ext/zend_test/test.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "fiber.h"
2727
#include "zend_attributes.h"
2828
#include "zend_enum.h"
29+
#include "zend_interfaces.h"
2930
#include "zend_weakrefs.h"
3031
#include "Zend/Optimizer/zend_optimizer.h"
3132
#include "test_arginfo.h"
@@ -277,6 +278,40 @@ static ZEND_FUNCTION(zend_iterable)
277278
ZEND_PARSE_PARAMETERS_END();
278279
}
279280

281+
/* Call a method on a class or object using zend_call_method() */
282+
static ZEND_FUNCTION(zend_call_method)
283+
{
284+
zval *class_or_object;
285+
zend_string *method_name;
286+
zval *arg1 = NULL, *arg2 = NULL;
287+
zend_object *obj = NULL;
288+
zend_class_entry *ce = NULL;
289+
int argc = ZEND_NUM_ARGS();
290+
291+
ZEND_PARSE_PARAMETERS_START(2, 4)
292+
Z_PARAM_ZVAL(class_or_object)
293+
Z_PARAM_STR(method_name)
294+
Z_PARAM_OPTIONAL
295+
Z_PARAM_ZVAL(arg1)
296+
Z_PARAM_ZVAL(arg2)
297+
ZEND_PARSE_PARAMETERS_END();
298+
299+
if (Z_TYPE_P(class_or_object) == IS_OBJECT) {
300+
obj = Z_OBJ_P(class_or_object);
301+
ce = obj->ce;
302+
} else {
303+
ZEND_ASSERT(Z_TYPE_P(class_or_object) == IS_STRING);
304+
ce = zend_lookup_class(Z_STR_P(class_or_object));
305+
if (!ce) {
306+
zend_error(E_ERROR, "Unknown class '%s'", Z_STRVAL_P(class_or_object));
307+
return;
308+
}
309+
}
310+
311+
ZEND_ASSERT((argc >= 2) && (argc <= 4));
312+
zend_call_method(obj, ce, NULL, ZSTR_VAL(method_name), ZSTR_LEN(method_name), return_value, argc - 2, arg1, arg2);
313+
}
314+
280315
static ZEND_FUNCTION(zend_get_unit_enum)
281316
{
282317
ZEND_PARSE_PARAMETERS_NONE();

ext/zend_test/test.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ function zend_get_unit_enum(): ZendTestUnitEnum {}
113113
function zend_test_parameter_with_attribute(string $parameter): int {}
114114

115115
function zend_get_current_func_name(): string {}
116+
117+
function zend_call_method(object|string $clsOrObject, string $method, mixed $arg1 = null, mixed $arg2 = null): mixed {}
116118
}
117119

118120
namespace ZendTestNS {

ext/zend_test/test_arginfo.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 6d9ff0e2263a39420dd157206331a9e897d9e775 */
2+
* Stub hash: 1be3dba6b0638764bcf00cd695cb88a3e8a0a530 */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0)
55
ZEND_END_ARG_INFO()
@@ -72,6 +72,13 @@ ZEND_END_ARG_INFO()
7272
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_get_current_func_name, 0, 0, IS_STRING, 0)
7373
ZEND_END_ARG_INFO()
7474

75+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_call_method, 0, 2, IS_MIXED, 0)
76+
ZEND_ARG_TYPE_MASK(0, clsOrObject, MAY_BE_OBJECT|MAY_BE_STRING, NULL)
77+
ZEND_ARG_TYPE_INFO(0, method, IS_STRING, 0)
78+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_MIXED, 0, "null")
79+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_MIXED, 0, "null")
80+
ZEND_END_ARG_INFO()
81+
7582
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ZendTestNS2_ZendSubNS_namespaced_func, 0, 0, _IS_BOOL, 0)
7683
ZEND_END_ARG_INFO()
7784

@@ -128,6 +135,7 @@ static ZEND_FUNCTION(zend_weakmap_dump);
128135
static ZEND_FUNCTION(zend_get_unit_enum);
129136
static ZEND_FUNCTION(zend_test_parameter_with_attribute);
130137
static ZEND_FUNCTION(zend_get_current_func_name);
138+
static ZEND_FUNCTION(zend_call_method);
131139
static ZEND_FUNCTION(namespaced_func);
132140
static ZEND_METHOD(_ZendTestClass, is_object);
133141
static ZEND_METHOD(_ZendTestClass, __toString);
@@ -164,6 +172,7 @@ static const zend_function_entry ext_functions[] = {
164172
ZEND_FE(zend_get_unit_enum, arginfo_zend_get_unit_enum)
165173
ZEND_FE(zend_test_parameter_with_attribute, arginfo_zend_test_parameter_with_attribute)
166174
ZEND_FE(zend_get_current_func_name, arginfo_zend_get_current_func_name)
175+
ZEND_FE(zend_call_method, arginfo_zend_call_method)
167176
ZEND_NS_FE("ZendTestNS2\\ZendSubNS", namespaced_func, arginfo_ZendTestNS2_ZendSubNS_namespaced_func)
168177
ZEND_FE_END
169178
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Calling a builtin function with 'static' return type from internal code
3+
--FILE--
4+
<?php
5+
6+
enum IntIntStaticInt : int {
7+
case Life = 42;
8+
}
9+
10+
enum IntIntStaticString : string {
11+
case ThanksFor = "all the fish";
12+
}
13+
14+
var_dump(zend_call_method("IntIntStaticInt", "from", 42));
15+
var_dump(zend_call_method("IntIntStaticInt", "tryFrom", 42));
16+
var_dump(zend_call_method("IntIntStaticString", "from", "all the fish"));
17+
var_dump(zend_call_method("IntIntStaticString", "tryFrom", "all the fish"));
18+
19+
class StillReturnsStatic extends _ZendTestClass {}
20+
21+
var_dump(get_class(zend_call_method("_ZendTestClass", "returnsStatic")));
22+
var_dump(get_class(zend_call_method("StillReturnsStatic", "returnsStatic")));
23+
24+
--EXPECT--
25+
enum(IntIntStaticInt::Life)
26+
enum(IntIntStaticInt::Life)
27+
enum(IntIntStaticString::ThanksFor)
28+
enum(IntIntStaticString::ThanksFor)
29+
string(14) "_ZendTestClass"
30+
string(18) "StillReturnsStatic"

0 commit comments

Comments
 (0)