Skip to content

Commit 0ce9b5f

Browse files
committed
Don't check for throwing calls in sccp function evaluation
We only need to reject functions that could warn (or have runtime dependent behavior). If a function can throw in some cases, just let it and discard the result.
1 parent a505fc6 commit 0ce9b5f

File tree

1 file changed

+121
-200
lines changed

1 file changed

+121
-200
lines changed

ext/opcache/Optimizer/sccp.c

Lines changed: 121 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -781,253 +781,174 @@ static inline int ct_eval_array_key_exists(zval *result, zval *op1, zval *op2) {
781781
return SUCCESS;
782782
}
783783

784-
/* The functions chosen here are simple to implement and either likely to affect a branch,
785-
* or just happened to be commonly used with constant operands in WP (need to test other
786-
* applications as well, of course). */
787-
static inline int ct_eval_func_call(
788-
zval *result, zend_string *name, uint32_t num_args, zval **args) {
789-
uint32_t i;
790-
zend_execute_data *execute_data, *prev_execute_data;
791-
zend_function *func;
792-
bool overflow;
793-
794-
if (num_args == 0) {
795-
if (zend_string_equals_literal(name, "php_sapi_name")
796-
|| zend_string_equals_literal(name, "imagetypes")
797-
|| zend_string_equals_literal(name, "phpversion")) {
798-
/* pass */
799-
} else {
800-
return FAILURE;
801-
}
802-
} else if (num_args == 1) {
803-
if (zend_string_equals_literal(name, "chr")) {
804-
zend_long c;
805-
if (Z_TYPE_P(args[0]) != IS_LONG) {
806-
return FAILURE;
807-
}
808-
809-
c = Z_LVAL_P(args[0]) & 0xff;
810-
ZVAL_CHAR(result, c);
811-
return SUCCESS;
812-
} else if (zend_string_equals_literal(name, "count")) {
813-
if (Z_TYPE_P(args[0]) != IS_ARRAY) {
814-
return FAILURE;
815-
}
816-
817-
ZVAL_LONG(result, zend_hash_num_elements(Z_ARRVAL_P(args[0])));
818-
return SUCCESS;
819-
} else if (zend_string_equals_literal(name, "ini_get")) {
820-
zend_ini_entry *ini_entry;
821-
822-
if (Z_TYPE_P(args[0]) != IS_STRING) {
823-
return FAILURE;
824-
}
825-
826-
ini_entry = zend_hash_find_ptr(EG(ini_directives), Z_STR_P(args[0]));
827-
if (!ini_entry) {
828-
ZVAL_FALSE(result);
829-
} else if (ini_entry->modifiable != ZEND_INI_SYSTEM) {
830-
return FAILURE;
831-
} else if (ini_entry->value) {
832-
ZVAL_STR_COPY(result, ini_entry->value);
833-
} else {
834-
ZVAL_EMPTY_STRING(result);
835-
}
836-
return SUCCESS;
837-
} else if (zend_string_equals_literal(name, "trim")
838-
|| zend_string_equals_literal(name, "rtrim")
839-
|| zend_string_equals_literal(name, "ltrim")
840-
|| zend_string_equals_literal(name, "str_split")
841-
|| zend_string_equals_literal(name, "preg_quote")
842-
|| zend_string_equals_literal(name, "base64_encode")
843-
|| zend_string_equals_literal(name, "base64_decode")
844-
|| zend_string_equals_literal(name, "urlencode")
845-
|| zend_string_equals_literal(name, "urldecode")
846-
|| zend_string_equals_literal(name, "rawurlencode")
847-
|| zend_string_equals_literal(name, "rawurldecode")
848-
|| zend_string_equals_literal(name, "php_uname")) {
849-
if (Z_TYPE_P(args[0]) != IS_STRING) {
850-
return FAILURE;
851-
}
852-
/* pass */
853-
} else if (zend_string_equals_literal(name, "array_keys")
854-
|| zend_string_equals_literal(name, "array_values")) {
855-
if (Z_TYPE_P(args[0]) != IS_ARRAY) {
856-
return FAILURE;
857-
}
858-
/* pass */
859-
} else if (zend_string_equals_literal(name, "array_flip")) {
784+
static zend_bool can_ct_eval_func_call(zend_string *name, uint32_t num_args, zval **args) {
785+
/* Functions that can be evaluated independently of what the arguments are.
786+
* It's okay if these functions throw on invalid arguments, but they should not warn. */
787+
if (false
788+
|| zend_string_equals_literal(name, "array_diff")
789+
|| zend_string_equals_literal(name, "array_diff_assoc")
790+
|| zend_string_equals_literal(name, "array_diff_key")
791+
|| zend_string_equals_literal(name, "array_key_exists")
792+
|| zend_string_equals_literal(name, "array_keys")
793+
|| zend_string_equals_literal(name, "array_merge")
794+
|| zend_string_equals_literal(name, "array_merge_recursive")
795+
|| zend_string_equals_literal(name, "array_replace")
796+
|| zend_string_equals_literal(name, "array_replace_recursive")
797+
|| zend_string_equals_literal(name, "array_values")
798+
|| zend_string_equals_literal(name, "base64_decode")
799+
|| zend_string_equals_literal(name, "base64_encode")
800+
|| zend_string_equals_literal(name, "imagetypes")
801+
|| zend_string_equals_literal(name, "in_array")
802+
|| zend_string_equals_literal(name, "ltrim")
803+
|| zend_string_equals_literal(name, "php_sapi_name")
804+
|| zend_string_equals_literal(name, "php_uname")
805+
|| zend_string_equals_literal(name, "phpversion")
806+
|| zend_string_equals_literal(name, "pow")
807+
|| zend_string_equals_literal(name, "preg_quote")
808+
|| zend_string_equals_literal(name, "rawurldecode")
809+
|| zend_string_equals_literal(name, "rawurlencode")
810+
|| zend_string_equals_literal(name, "rtrim")
811+
|| zend_string_equals_literal(name, "serialize")
812+
|| zend_string_equals_literal(name, "str_contains")
813+
|| zend_string_equals_literal(name, "str_ends_with")
814+
|| zend_string_equals_literal(name, "str_split")
815+
|| zend_string_equals_literal(name, "str_split")
816+
|| zend_string_equals_literal(name, "str_starts_with")
817+
|| zend_string_equals_literal(name, "strpos")
818+
|| zend_string_equals_literal(name, "substr")
819+
|| zend_string_equals_literal(name, "trim")
820+
|| zend_string_equals_literal(name, "urldecode")
821+
|| zend_string_equals_literal(name, "urlencode")
822+
|| zend_string_equals_literal(name, "version_compare")
823+
) {
824+
return true;
825+
}
826+
827+
/* For the following functions we need to check arguments to prevent warnings during
828+
* evaluation. */
829+
if (num_args == 1) {
830+
if (zend_string_equals_literal(name, "array_flip")) {
860831
zval *entry;
861832

862833
if (Z_TYPE_P(args[0]) != IS_ARRAY) {
863-
return FAILURE;
834+
return false;
864835
}
865836
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
837+
/* Throws warning for non int/string values. */
866838
if (Z_TYPE_P(entry) != IS_LONG && Z_TYPE_P(entry) != IS_STRING) {
867-
return FAILURE;
839+
return false;
868840
}
869841
} ZEND_HASH_FOREACH_END();
870-
/* pass */
871-
} else if (zend_string_equals_literal(name, "implode")) {
842+
return true;
843+
}
844+
if (zend_string_equals_literal(name, "implode")) {
872845
zval *entry;
873846

874847
if (Z_TYPE_P(args[0]) != IS_ARRAY) {
875-
return FAILURE;
848+
return false;
876849
}
877850

878851
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
852+
/* May throw warning during conversion to string. */
879853
if (Z_TYPE_P(entry) > IS_STRING) {
880-
return FAILURE;
854+
return false;
881855
}
882856
} ZEND_HASH_FOREACH_END();
883-
/* pass */
884-
} else if (zend_string_equals_literal(name, "serialize")) {
885-
/* pass */
886-
} else {
887-
return FAILURE;
857+
return true;
888858
}
889-
} else if (num_args == 2) {
890-
if (zend_string_equals_literal(name, "in_array")) {
891-
if (Z_TYPE_P(args[1]) != IS_ARRAY) {
892-
return FAILURE;
893-
}
894-
/* pass */
895-
} else if (zend_string_equals_literal(name, "str_split")) {
896-
if (Z_TYPE_P(args[0]) != IS_STRING
897-
|| Z_TYPE_P(args[1]) != IS_LONG
898-
|| Z_LVAL_P(args[1]) <= 0) {
899-
return FAILURE;
900-
}
901-
/* pass */
902-
} else if (zend_string_equals_literal(name, "array_key_exists")) {
903-
if (Z_TYPE_P(args[1]) != IS_ARRAY
904-
|| (Z_TYPE_P(args[0]) != IS_LONG
905-
&& Z_TYPE_P(args[0]) != IS_STRING
906-
&& Z_TYPE_P(args[0]) != IS_NULL)) {
907-
return FAILURE;
908-
}
909-
/* pass */
910-
} else if (zend_string_equals_literal(name, "trim")
911-
|| zend_string_equals_literal(name, "rtrim")
912-
|| zend_string_equals_literal(name, "ltrim")
913-
|| zend_string_equals_literal(name, "preg_quote")) {
914-
if (Z_TYPE_P(args[0]) != IS_STRING
915-
|| Z_TYPE_P(args[1]) != IS_STRING) {
916-
return FAILURE;
917-
}
918-
/* pass */
919-
} else if (zend_string_equals_literal(name, "str_repeat")) {
920-
if (Z_TYPE_P(args[0]) != IS_STRING
921-
|| Z_TYPE_P(args[1]) != IS_LONG
922-
|| zend_safe_address(Z_STRLEN_P(args[0]), Z_LVAL_P(args[1]), 0, &overflow) > 64 * 1024
923-
|| overflow) {
924-
return FAILURE;
925-
}
926-
/* pass */
927-
} else if (zend_string_equals_literal(name, "array_merge")
928-
|| zend_string_equals_literal(name, "array_replace")
929-
|| zend_string_equals_literal(name, "array_merge_recursive")
930-
|| zend_string_equals_literal(name, "array_replace_recursive")
931-
|| zend_string_equals_literal(name, "array_diff")
932-
|| zend_string_equals_literal(name, "array_diff_assoc")
933-
|| zend_string_equals_literal(name, "array_diff_key")) {
934-
for (i = 0; i < num_args; i++) {
935-
if (Z_TYPE_P(args[i]) != IS_ARRAY) {
936-
return FAILURE;
937-
}
938-
}
939-
/* pass */
859+
return false;
860+
}
861+
862+
if (num_args == 2) {
863+
if (zend_string_equals_literal(name, "str_repeat")) {
864+
/* Avoid creating overly large strings at compile-time. */
865+
bool overflow;
866+
return Z_TYPE_P(args[0]) == IS_STRING
867+
&& Z_TYPE_P(args[1]) == IS_LONG
868+
&& zend_safe_address(Z_STRLEN_P(args[0]), Z_LVAL_P(args[1]), 0, &overflow) < 64 * 1024
869+
&& !overflow;
940870
} else if (zend_string_equals_literal(name, "implode")) {
941871
zval *entry;
942872

943873
if ((Z_TYPE_P(args[0]) != IS_STRING || Z_TYPE_P(args[1]) != IS_ARRAY)
944874
&& (Z_TYPE_P(args[0]) != IS_ARRAY || Z_TYPE_P(args[1]) != IS_STRING)) {
945-
return FAILURE;
875+
return false;
946876
}
947877

878+
/* May throw warning during conversion to string. */
948879
if (Z_TYPE_P(args[0]) == IS_ARRAY) {
949880
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
950881
if (Z_TYPE_P(entry) > IS_STRING) {
951-
return FAILURE;
882+
return false;
952883
}
953884
} ZEND_HASH_FOREACH_END();
954885
} else {
955886
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[1]), entry) {
956887
if (Z_TYPE_P(entry) > IS_STRING) {
957-
return FAILURE;
888+
return false;
958889
}
959890
} ZEND_HASH_FOREACH_END();
960891
}
961-
/* pass */
962-
} else if (zend_string_equals_literal(name, "strpos")
963-
|| zend_string_equals_literal(name, "str_contains")
964-
|| zend_string_equals_literal(name, "str_starts_with")
965-
|| zend_string_equals_literal(name, "str_ends_with")
966-
|| zend_string_equals_literal(name, "version_compare")) {
967-
if (Z_TYPE_P(args[0]) != IS_STRING
968-
|| Z_TYPE_P(args[1]) != IS_STRING) {
969-
return FAILURE;
970-
}
971-
/* pass */
972-
} else if (zend_string_equals_literal(name, "substr")) {
973-
if (Z_TYPE_P(args[0]) != IS_STRING
974-
|| Z_TYPE_P(args[1]) != IS_LONG) {
975-
return FAILURE;
976-
}
977-
/* pass */
978-
} else if (zend_string_equals_literal(name, "pow")) {
979-
if ((Z_TYPE_P(args[0]) != IS_LONG && Z_TYPE_P(args[0]) != IS_DOUBLE)
980-
|| (Z_TYPE_P(args[1]) != IS_LONG && Z_TYPE_P(args[1]) != IS_DOUBLE)) {
981-
return FAILURE;
982-
}
983-
/* pass */
984-
} else {
985-
return FAILURE;
892+
return true;
986893
}
987-
} else if (num_args == 3) {
988-
if (zend_string_equals_literal(name, "in_array")) {
989-
if (Z_TYPE_P(args[1]) != IS_ARRAY
990-
|| (Z_TYPE_P(args[2]) != IS_FALSE
991-
&& Z_TYPE_P(args[2]) != IS_TRUE)) {
894+
return false;
895+
}
896+
897+
return false;
898+
}
899+
900+
/* The functions chosen here are simple to implement and either likely to affect a branch,
901+
* or just happened to be commonly used with constant operands in WP (need to test other
902+
* applications as well, of course). */
903+
static inline int ct_eval_func_call(
904+
zval *result, zend_string *name, uint32_t num_args, zval **args) {
905+
uint32_t i;
906+
zend_execute_data *execute_data, *prev_execute_data;
907+
zend_function *func = zend_hash_find_ptr(CG(function_table), name);
908+
if (!func || func->type != ZEND_INTERNAL_FUNCTION) {
909+
return FAILURE;
910+
}
911+
912+
if (num_args == 1) {
913+
/* Handle a few functions for which we manually implement evaluation here. */
914+
if (zend_string_equals_literal(name, "chr")) {
915+
zend_long c;
916+
if (Z_TYPE_P(args[0]) != IS_LONG) {
992917
return FAILURE;
993918
}
994-
/* pass */
995-
} else if (zend_string_equals_literal(name, "array_merge")
996-
|| zend_string_equals_literal(name, "array_replace")
997-
|| zend_string_equals_literal(name, "array_merge_recursive")
998-
|| zend_string_equals_literal(name, "array_replace_recursive")
999-
|| zend_string_equals_literal(name, "array_diff")
1000-
|| zend_string_equals_literal(name, "array_diff_assoc")
1001-
|| zend_string_equals_literal(name, "array_diff_key")) {
1002-
for (i = 0; i < num_args; i++) {
1003-
if (Z_TYPE_P(args[i]) != IS_ARRAY) {
1004-
return FAILURE;
1005-
}
919+
920+
c = Z_LVAL_P(args[0]) & 0xff;
921+
ZVAL_CHAR(result, c);
922+
return SUCCESS;
923+
} else if (zend_string_equals_literal(name, "count")) {
924+
if (Z_TYPE_P(args[0]) != IS_ARRAY) {
925+
return FAILURE;
1006926
}
1007-
/* pass */
1008-
} else if (zend_string_equals_literal(name, "version_compare")) {
1009-
if (Z_TYPE_P(args[0]) != IS_STRING
1010-
|| Z_TYPE_P(args[1]) != IS_STRING
1011-
|| Z_TYPE_P(args[2]) != IS_STRING) {
927+
928+
ZVAL_LONG(result, zend_hash_num_elements(Z_ARRVAL_P(args[0])));
929+
return SUCCESS;
930+
} else if (zend_string_equals_literal(name, "ini_get")) {
931+
zend_ini_entry *ini_entry;
932+
933+
if (Z_TYPE_P(args[0]) != IS_STRING) {
1012934
return FAILURE;
1013935
}
1014-
/* pass */
1015-
} else if (zend_string_equals_literal(name, "substr")) {
1016-
if (Z_TYPE_P(args[0]) != IS_STRING
1017-
|| Z_TYPE_P(args[1]) != IS_LONG
1018-
|| Z_TYPE_P(args[2]) != IS_LONG) {
936+
937+
ini_entry = zend_hash_find_ptr(EG(ini_directives), Z_STR_P(args[0]));
938+
if (!ini_entry) {
939+
ZVAL_FALSE(result);
940+
} else if (ini_entry->modifiable != ZEND_INI_SYSTEM) {
1019941
return FAILURE;
942+
} else if (ini_entry->value) {
943+
ZVAL_STR_COPY(result, ini_entry->value);
944+
} else {
945+
ZVAL_EMPTY_STRING(result);
1020946
}
1021-
/* pass */
1022-
} else {
1023-
return FAILURE;
947+
return SUCCESS;
1024948
}
1025-
} else {
1026-
return FAILURE;
1027949
}
1028950

1029-
func = zend_hash_find_ptr(CG(function_table), name);
1030-
if (!func || func->type != ZEND_INTERNAL_FUNCTION) {
951+
if (!can_ct_eval_func_call(name, num_args, args)) {
1031952
return FAILURE;
1032953
}
1033954

0 commit comments

Comments
 (0)