Skip to content

Commit 5b3809e

Browse files
committed
Respect strict_types during sccp function call evaluation
Similar to what we do with attributes, add a dummy call frame on which we can set the strict_types flag.
1 parent 0ce9b5f commit 5b3809e

File tree

3 files changed

+30
-6
lines changed

3 files changed

+30
-6
lines changed

ext/opcache/Optimizer/sccp.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,6 @@ static zend_bool can_ct_eval_func_call(zend_string *name, uint32_t num_args, zva
812812
|| zend_string_equals_literal(name, "str_contains")
813813
|| zend_string_equals_literal(name, "str_ends_with")
814814
|| zend_string_equals_literal(name, "str_split")
815-
|| zend_string_equals_literal(name, "str_split")
816815
|| zend_string_equals_literal(name, "str_starts_with")
817816
|| zend_string_equals_literal(name, "strpos")
818817
|| zend_string_equals_literal(name, "substr")
@@ -901,9 +900,8 @@ static zend_bool can_ct_eval_func_call(zend_string *name, uint32_t num_args, zva
901900
* or just happened to be commonly used with constant operands in WP (need to test other
902901
* applications as well, of course). */
903902
static inline int ct_eval_func_call(
904-
zval *result, zend_string *name, uint32_t num_args, zval **args) {
903+
zend_op_array *op_array, zval *result, zend_string *name, uint32_t num_args, zval **args) {
905904
uint32_t i;
906-
zend_execute_data *execute_data, *prev_execute_data;
907905
zend_function *func = zend_hash_find_ptr(CG(function_table), name);
908906
if (!func || func->type != ZEND_INTERNAL_FUNCTION) {
909907
return FAILURE;
@@ -952,9 +950,20 @@ static inline int ct_eval_func_call(
952950
return FAILURE;
953951
}
954952

953+
zend_execute_data *prev_execute_data = EG(current_execute_data);
954+
zend_execute_data *execute_data, dummy_frame;
955+
zend_op dummy_opline;
956+
957+
/* Add a dummy frame to get the correct strict_types behavior. */
958+
memset(&dummy_frame, 0, sizeof(zend_execute_data));
959+
memset(&dummy_opline, 0, sizeof(zend_op));
960+
dummy_frame.func = (zend_function *) op_array;
961+
dummy_frame.opline = &dummy_opline;
962+
dummy_opline.opcode = ZEND_DO_FCALL;
963+
955964
execute_data = safe_emalloc(num_args, sizeof(zval), ZEND_CALL_FRAME_SLOT * sizeof(zval));
956965
memset(execute_data, 0, sizeof(zend_execute_data));
957-
prev_execute_data = EG(current_execute_data);
966+
execute_data->prev_execute_data = &dummy_frame;
958967
EG(current_execute_data) = execute_data;
959968

960969
EX(func) = func;
@@ -1832,7 +1841,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
18321841
break;
18331842
}
18341843

1835-
if (ct_eval_func_call(&zv, Z_STR_P(name), call->num_args, args) == SUCCESS) {
1844+
if (ct_eval_func_call(scdf->op_array, &zv, Z_STR_P(name), call->num_args, args) == SUCCESS) {
18361845
SET_RESULT(result, &zv);
18371846
zval_ptr_dtor_nogc(&zv);
18381847
break;

ext/opcache/tests/opt/sccp_exception2.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ require __DIR__ . '/sccp_exception2.inc';
88
Fatal error: Uncaught ValueError: version_compare(): Argument #3 ($operator) must be a valid comparison operator in %s:%d
99
Stack trace:
1010
#0 %s(%d): version_compare('1.2', '2.1', '??')
11-
#1 %s(%d): require('/home/nikic/php...')
11+
#1 %s(%d): require('%s')
1212
#2 {main}
1313
thrown in %s on line %d
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Exception thrown during SCCP evaluation, strict types variation
3+
--FILE--
4+
<?php
5+
6+
declare(strict_types=1);
7+
var_dump(str_contains("123", 1));
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Uncaught TypeError: str_contains(): Argument #2 ($needle) must be of type string, int given in %s:%d
12+
Stack trace:
13+
#0 %s(%d): str_contains('123', 1)
14+
#1 {main}
15+
thrown in %s on line %d

0 commit comments

Comments
 (0)