Skip to content

Commit 4c088c5

Browse files
committed
Handle warnings during sccp function evaluation
Some upcoming changes like https://wiki.php.net/rfc/deprecate_null_to_scalar_internal_arg will make it somewhat inconvenient to determine whether a given function invocation will generate a diagnostic. Rather than trying to exclude this in advance, call the function with diagnostics suppressed, and check whether anything was thrown. This adds a new EG flag that is kept specific to the SCCP use-case. This does not use the error_cb hook as it is a (non-TLS) global, and doesn't fully suppress error handling besides. Test this by removing the in advance checks for implode and array_flip.
1 parent 4224b70 commit 4c088c5

File tree

4 files changed

+49
-60
lines changed

4 files changed

+49
-60
lines changed

Zend/Optimizer/sccp.c

Lines changed: 12 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,7 @@ static bool can_ct_eval_func_call(zend_string *name, uint32_t num_args, zval **a
788788
|| zend_string_equals_literal(name, "array_diff")
789789
|| zend_string_equals_literal(name, "array_diff_assoc")
790790
|| zend_string_equals_literal(name, "array_diff_key")
791+
|| zend_string_equals_literal(name, "array_flip")
791792
|| zend_string_equals_literal(name, "array_is_list")
792793
|| zend_string_equals_literal(name, "array_key_exists")
793794
|| zend_string_equals_literal(name, "array_keys")
@@ -804,6 +805,7 @@ static bool can_ct_eval_func_call(zend_string *name, uint32_t num_args, zval **a
804805
#endif
805806
|| zend_string_equals_literal(name, "imagetypes")
806807
|| zend_string_equals_literal(name, "in_array")
808+
|| zend_string_equals_literal(name, "implode")
807809
|| zend_string_equals_literal(name, "ltrim")
808810
|| zend_string_equals_literal(name, "php_sapi_name")
809811
|| zend_string_equals_literal(name, "php_uname")
@@ -828,41 +830,6 @@ static bool can_ct_eval_func_call(zend_string *name, uint32_t num_args, zval **a
828830
return true;
829831
}
830832

831-
/* For the following functions we need to check arguments to prevent warnings during
832-
* evaluation. */
833-
if (num_args == 1) {
834-
if (zend_string_equals_literal(name, "array_flip")) {
835-
zval *entry;
836-
837-
if (Z_TYPE_P(args[0]) != IS_ARRAY) {
838-
return false;
839-
}
840-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
841-
/* Throws warning for non int/string values. */
842-
if (Z_TYPE_P(entry) != IS_LONG && Z_TYPE_P(entry) != IS_STRING) {
843-
return false;
844-
}
845-
} ZEND_HASH_FOREACH_END();
846-
return true;
847-
}
848-
if (zend_string_equals_literal(name, "implode")) {
849-
zval *entry;
850-
851-
if (Z_TYPE_P(args[0]) != IS_ARRAY) {
852-
return false;
853-
}
854-
855-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
856-
/* May throw warning during conversion to string. */
857-
if (Z_TYPE_P(entry) > IS_STRING) {
858-
return false;
859-
}
860-
} ZEND_HASH_FOREACH_END();
861-
return true;
862-
}
863-
return false;
864-
}
865-
866833
if (num_args == 2) {
867834
if (zend_string_equals_literal(name, "str_repeat")) {
868835
/* Avoid creating overly large strings at compile-time. */
@@ -871,29 +838,6 @@ static bool can_ct_eval_func_call(zend_string *name, uint32_t num_args, zval **a
871838
&& Z_TYPE_P(args[1]) == IS_LONG
872839
&& zend_safe_address(Z_STRLEN_P(args[0]), Z_LVAL_P(args[1]), 0, &overflow) < 64 * 1024
873840
&& !overflow;
874-
} else if (zend_string_equals_literal(name, "implode")) {
875-
zval *entry;
876-
877-
if ((Z_TYPE_P(args[0]) != IS_STRING || Z_TYPE_P(args[1]) != IS_ARRAY)
878-
&& (Z_TYPE_P(args[0]) != IS_ARRAY || Z_TYPE_P(args[1]) != IS_STRING)) {
879-
return false;
880-
}
881-
882-
/* May throw warning during conversion to string. */
883-
if (Z_TYPE_P(args[0]) == IS_ARRAY) {
884-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
885-
if (Z_TYPE_P(entry) > IS_STRING) {
886-
return false;
887-
}
888-
} ZEND_HASH_FOREACH_END();
889-
} else {
890-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[1]), entry) {
891-
if (Z_TYPE_P(entry) > IS_STRING) {
892-
return false;
893-
}
894-
} ZEND_HASH_FOREACH_END();
895-
}
896-
return true;
897841
}
898842
return false;
899843
}
@@ -971,6 +915,10 @@ static inline int ct_eval_func_call(
971915
execute_data->prev_execute_data = &dummy_frame;
972916
EG(current_execute_data) = execute_data;
973917

918+
/* Enable suppression and counting of warnings. */
919+
ZEND_ASSERT(EG(capture_warnings_during_sccp) == 0);
920+
EG(capture_warnings_during_sccp) = 1;
921+
974922
EX(func) = func;
975923
EX_NUM_ARGS() = num_args;
976924
for (i = 0; i < num_args; i++) {
@@ -989,6 +937,12 @@ static inline int ct_eval_func_call(
989937
retval = FAILURE;
990938
}
991939

940+
if (EG(capture_warnings_during_sccp) > 1) {
941+
zval_ptr_dtor(result);
942+
retval = FAILURE;
943+
}
944+
EG(capture_warnings_during_sccp) = 0;
945+
992946
efree(execute_data);
993947
EG(current_execute_data) = prev_execute_data;
994948
return retval;

Zend/zend.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{
714714
zend_init_exception_op();
715715
zend_init_call_trampoline_op();
716716
memset(&executor_globals->trampoline, 0, sizeof(zend_op_array));
717-
executor_globals->lambda_count = 0;
717+
executor_globals->warnings_during_sccp = 0;
718718
ZVAL_UNDEF(&executor_globals->user_error_handler);
719719
ZVAL_UNDEF(&executor_globals->user_exception_handler);
720720
executor_globals->in_autoload = NULL;
@@ -1299,6 +1299,14 @@ static ZEND_COLD void zend_error_impl(
12991299
zend_stack delayed_oplines_stack;
13001300
int type = orig_type & E_ALL;
13011301

1302+
/* If we're executing a function during SCCP, count any warnings that may be emitted,
1303+
* but don't perform any other error handling. */
1304+
if (EG(capture_warnings_during_sccp)) {
1305+
ZEND_ASSERT(!(type & E_FATAL_ERRORS) && "Fatal error during SCCP");
1306+
EG(capture_warnings_during_sccp)++;
1307+
return;
1308+
}
1309+
13021310
/* Report about uncaught exception in case of fatal errors */
13031311
if (EG(exception)) {
13041312
zend_execute_data *ex;

Zend/zend_globals.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ struct _zend_executor_globals {
207207
/* timeout support */
208208
zend_long timeout_seconds;
209209

210-
int lambda_count;
210+
int capture_warnings_during_sccp;
211211

212212
HashTable *ini_directives;
213213
HashTable *modified_ini_directives;

ext/opcache/tests/opt/sccp_033.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
SCCP: Warning during evaluation of function
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.optimization_level=-1
7+
--SKIPIF--
8+
<?php require_once('skipif.inc'); ?>
9+
--FILE--
10+
<?php
11+
12+
function test() {
13+
$array = ["foo", []];
14+
return implode(",", $array);
15+
}
16+
17+
echo "Before\n";
18+
var_dump(test());
19+
echo "After\n";
20+
21+
?>
22+
--EXPECTF--
23+
Before
24+
25+
Warning: Array to string conversion in %s on line %d
26+
string(9) "foo,Array"
27+
After

0 commit comments

Comments
 (0)