diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2019-12-06 11:07:57 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2019-12-06 11:07:57 +0100 |
commit | 249e49092d0e7f4b38a83e59947797d4e255991f (patch) | |
tree | fb8717b9ca1ed2b4acdfdc60ceabb80d44a6786f | |
parent | fac43d6515adb932083edeacd40f87d6b8393d66 (diff) | |
download | php-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.phpt | 9 | ||||
-rw-r--r-- | Zend/zend_compile.c | 27 |
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; } |