Skip to content

Commit

Permalink
Fix compile-time/run-time discrepancies with unary operators
Browse files Browse the repository at this point in the history
This addresses two issues:
 * ~ throws for a number of types, and we should not compile-time
   evaluate in that case. Add a check similar to what we do for
   binary ops.
 * Unary +/- may produce a different error message due to
   canonicalization of the constant operand to the RHS. To avoid
   this, put the constant operand on the RHS right away.

Fixes oss-fuzz #25649.
  • Loading branch information
nikic committed Sep 15, 2020
1 parent f5bbb04 commit 16b9f19
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,9 @@ Unsupported operand types: int ^ string

Warning: A non-numeric value encountered in %s on line %d
int(149)
Unsupported operand types: int * string
Unsupported operand types: string * int
---

Warning: A non-numeric value encountered in %s on line %d
int(-151)
Unsupported operand types: int * string
Unsupported operand types: string * int
43 changes: 36 additions & 7 deletions Zend/tests/runtime_compile_time_binary_operands.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ memory_limit=256M
--FILE--
<?php

$operands = [
$binaryOperators = [
"==",
"!=",
"===",
Expand All @@ -31,6 +31,12 @@ $operands = [
"||",
"&&",
];
$unaryOperators = [
"~",
"-",
"+",
"!",
];

$input = [
0,
Expand Down Expand Up @@ -100,8 +106,7 @@ function makeParam($param) {
$c = 0;
$f = 0;

function prepareLine($op1, $op2, $cmp, $operator) {

function prepareBinaryLine($op1, $op2, $cmp, $operator) {
$op1_p = makeParam($op1);
$op2_p = makeParam($op2);

Expand All @@ -118,6 +123,22 @@ function prepareLine($op1, $op2, $cmp, $operator) {
}
return $line;
}
function prepareUnaryLine($op, $cmp, $operator) {
$op_p = makeParam($op);

$error = "echo '" . addcslashes("$operator $op_p", "\\'") . '\', "\n"; $f++;';

$compare = "@($operator $op_p)";
$line = "\$c++; ";
try {
$result = makeParam($cmp());
$line .= "if (" . ($result === "(NAN)" ? "!is_nan($compare)" : "$compare !== $result") . ") { $error }";
} catch (Error $e) {
$msg = makeParam($e->getMessage());
$line .= "try { $compare; $error } catch (Error \$e) { if (\$e->getMessage() !== $msg) { $error } }";
}
return $line;
}

$filename = __DIR__ . DIRECTORY_SEPARATOR . 'compare_binary_operands_temp.php';
$file = fopen($filename, "w");
Expand All @@ -126,14 +147,22 @@ fwrite($file, "<?php\n");

foreach ($input as $left) {
foreach ($input as $right) {
foreach ($operands as $operand) {
$line = prepareLine($left, $right, function() use ($left, $right, $operand) {
return eval("return @(\$left $operand \$right);");
}, $operand);
foreach ($binaryOperators as $operator) {
$line = prepareBinaryLine($left, $right, function() use ($left, $right, $operator) {
return eval("return @(\$left $operator \$right);");
}, $operator);
fwrite($file, $line . "\n");
}
}
}
foreach ($input as $right) {
foreach ($unaryOperators as $operator) {
$line = prepareUnaryLine($right, function() use ($right, $operator) {
return eval("return @($operator \$right);");
}, $operator);
fwrite($file, $line . "\n");
}
}

fclose($file);

Expand Down
40 changes: 27 additions & 13 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -7913,18 +7913,32 @@ static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode
}
/* }}} */

static inline void zend_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op) /* {{{ */
zend_bool zend_unary_op_produces_error(uint32_t opcode, zval *op)
{
if (opcode == ZEND_BW_NOT) {
return Z_TYPE_P(op) <= IS_TRUE || Z_TYPE_P(op) == IS_ARRAY;
}

return 0;
}

static inline zend_bool zend_try_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op) /* {{{ */
{
if (zend_unary_op_produces_error(opcode, op)) {
return 0;
}

unary_op_type fn = get_unary_op(opcode);
fn(result, op);
return 1;
}
/* }}} */

static inline zend_bool zend_try_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */
{
zval left;
ZVAL_LONG(&left, (kind == ZEND_AST_UNARY_PLUS) ? 1 : -1);
return zend_try_ct_eval_binary_op(result, ZEND_MUL, &left, op);
zval right;
ZVAL_LONG(&right, (kind == ZEND_AST_UNARY_PLUS) ? 1 : -1);
return zend_try_ct_eval_binary_op(result, ZEND_MUL, op, &right);
}
/* }}} */

Expand Down Expand Up @@ -8185,10 +8199,9 @@ void zend_compile_unary_op(znode *result, zend_ast *ast) /* {{{ */
znode expr_node;
zend_compile_expr(&expr_node, expr_ast);

if (expr_node.op_type == IS_CONST) {
if (expr_node.op_type == IS_CONST
&& zend_try_ct_eval_unary_op(&result->u.constant, opcode, &expr_node.u.constant)) {
result->op_type = IS_CONST;
zend_ct_eval_unary_op(&result->u.constant, opcode,
&expr_node.u.constant);
zval_ptr_dtor(&expr_node.u.constant);
return;
}
Expand All @@ -8200,8 +8213,7 @@ void zend_compile_unary_op(znode *result, zend_ast *ast) /* {{{ */
void zend_compile_unary_pm(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
znode expr_node;
znode lefthand_node;
znode expr_node, right_node;

ZEND_ASSERT(ast->kind == ZEND_AST_UNARY_PLUS || ast->kind == ZEND_AST_UNARY_MINUS);

Expand All @@ -8214,9 +8226,9 @@ void zend_compile_unary_pm(znode *result, zend_ast *ast) /* {{{ */
return;
}

lefthand_node.op_type = IS_CONST;
ZVAL_LONG(&lefthand_node.u.constant, (ast->kind == ZEND_AST_UNARY_PLUS) ? 1 : -1);
zend_emit_op_tmp(result, ZEND_MUL, &lefthand_node, &expr_node);
right_node.op_type = IS_CONST;
ZVAL_LONG(&right_node.u.constant, (ast->kind == ZEND_AST_UNARY_PLUS) ? 1 : -1);
zend_emit_op_tmp(result, ZEND_MUL, &expr_node, &right_node);
}
/* }}} */

Expand Down Expand Up @@ -9752,7 +9764,9 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
return;
}

zend_ct_eval_unary_op(&result, ast->attr, zend_ast_get_zval(ast->child[0]));
if (!zend_try_ct_eval_unary_op(&result, ast->attr, zend_ast_get_zval(ast->child[0]))) {
return;
}
break;
case ZEND_AST_UNARY_PLUS:
case ZEND_AST_UNARY_MINUS:
Expand Down

0 comments on commit 16b9f19

Please sign in to comment.