summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-12-06 11:07:57 +0100
committerNikita Popov <nikita.ppv@gmail.com>2019-12-06 11:07:57 +0100
commit249e49092d0e7f4b38a83e59947797d4e255991f (patch)
treefb8717b9ca1ed2b4acdfdc60ceabb80d44a6786f
parentfac43d6515adb932083edeacd40f87d6b8393d66 (diff)
downloadphp-git-249e49092d0e7f4b38a83e59947797d4e255991f.tar.gz
Fix constant evaluation of && and ||
The "return" in the for loop should have been a break on the switch, otherwise the result is just ignored... but because it prevents evaluation of the other operand, it also violates the invariant that everything has been constant evaluated, resulting in an assertion failure. The for loop isn't correct in any case though, because it's not legal to determine the result based on just the second operand, as the first one may have a side-effect that cannot be optimized away.
-rw-r--r--Zend/tests/const_eval_and.phpt9
-rw-r--r--Zend/zend_compile.c27
2 files changed, 24 insertions, 12 deletions
diff --git a/Zend/tests/const_eval_and.phpt b/Zend/tests/const_eval_and.phpt
new file mode 100644
index 0000000000..90917bade5
--- /dev/null
+++ b/Zend/tests/const_eval_and.phpt
@@ -0,0 +1,9 @@
+--TEST--
+Incorrect constant evaluation of and/or (OSS-Fuzz #19255)
+--FILE--
+<?php
+const C = 0 && __namespace__;
+var_dump(C);
+?>
+--EXPECT--
+bool(false)
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index c8199c1c14..776ad6ba9e 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -8549,25 +8549,28 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
case ZEND_AST_AND:
case ZEND_AST_OR:
{
- int i;
- for (i = 0; i <= 1; i++) {
- zend_eval_const_expr(&ast->child[i]);
- if (ast->child[i]->kind == ZEND_AST_ZVAL) {
- if (zend_is_true(zend_ast_get_zval(ast->child[i])) == (ast->kind == ZEND_AST_OR)) {
- ZVAL_BOOL(&result, ast->kind == ZEND_AST_OR);
- return;
- }
- }
+ zend_bool child0_is_true, child1_is_true;
+ zend_eval_const_expr(&ast->child[0]);
+ zend_eval_const_expr(&ast->child[1]);
+ if (ast->child[0]->kind != ZEND_AST_ZVAL) {
+ return;
}
- if (ast->child[0]->kind != ZEND_AST_ZVAL || ast->child[1]->kind != ZEND_AST_ZVAL) {
+ child0_is_true = zend_is_true(zend_ast_get_zval(ast->child[0]));
+ if (child0_is_true == (ast->kind == ZEND_AST_OR)) {
+ ZVAL_BOOL(&result, ast->kind == ZEND_AST_OR);
+ break;
+ }
+
+ if (ast->child[1]->kind != ZEND_AST_ZVAL) {
return;
}
+ child1_is_true = zend_is_true(zend_ast_get_zval(ast->child[1]));
if (ast->kind == ZEND_AST_OR) {
- ZVAL_BOOL(&result, zend_is_true(zend_ast_get_zval(ast->child[0])) || zend_is_true(zend_ast_get_zval(ast->child[1])));
+ ZVAL_BOOL(&result, child0_is_true || child1_is_true);
} else {
- ZVAL_BOOL(&result, zend_is_true(zend_ast_get_zval(ast->child[0])) && zend_is_true(zend_ast_get_zval(ast->child[1])));
+ ZVAL_BOOL(&result, child0_is_true && child1_is_true);
}
break;
}