Skip to content

Commit 16b9f19

Browse files
committed
Fix compile-time/run-time discrepancies with unary operators
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.
1 parent f5bbb04 commit 16b9f19

File tree

3 files changed

+65
-22
lines changed

3 files changed

+65
-22
lines changed

Zend/tests/numeric_strings/invalid_numeric_strings_must_generate_warning.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,9 @@ Unsupported operand types: int ^ string
198198

199199
Warning: A non-numeric value encountered in %s on line %d
200200
int(149)
201-
Unsupported operand types: int * string
201+
Unsupported operand types: string * int
202202
---
203203

204204
Warning: A non-numeric value encountered in %s on line %d
205205
int(-151)
206-
Unsupported operand types: int * string
206+
Unsupported operand types: string * int

Zend/tests/runtime_compile_time_binary_operands.phpt

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ memory_limit=256M
55
--FILE--
66
<?php
77

8-
$operands = [
8+
$binaryOperators = [
99
"==",
1010
"!=",
1111
"===",
@@ -31,6 +31,12 @@ $operands = [
3131
"||",
3232
"&&",
3333
];
34+
$unaryOperators = [
35+
"~",
36+
"-",
37+
"+",
38+
"!",
39+
];
3440

3541
$input = [
3642
0,
@@ -100,8 +106,7 @@ function makeParam($param) {
100106
$c = 0;
101107
$f = 0;
102108

103-
function prepareLine($op1, $op2, $cmp, $operator) {
104-
109+
function prepareBinaryLine($op1, $op2, $cmp, $operator) {
105110
$op1_p = makeParam($op1);
106111
$op2_p = makeParam($op2);
107112

@@ -118,6 +123,22 @@ function prepareLine($op1, $op2, $cmp, $operator) {
118123
}
119124
return $line;
120125
}
126+
function prepareUnaryLine($op, $cmp, $operator) {
127+
$op_p = makeParam($op);
128+
129+
$error = "echo '" . addcslashes("$operator $op_p", "\\'") . '\', "\n"; $f++;';
130+
131+
$compare = "@($operator $op_p)";
132+
$line = "\$c++; ";
133+
try {
134+
$result = makeParam($cmp());
135+
$line .= "if (" . ($result === "(NAN)" ? "!is_nan($compare)" : "$compare !== $result") . ") { $error }";
136+
} catch (Error $e) {
137+
$msg = makeParam($e->getMessage());
138+
$line .= "try { $compare; $error } catch (Error \$e) { if (\$e->getMessage() !== $msg) { $error } }";
139+
}
140+
return $line;
141+
}
121142

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

127148
foreach ($input as $left) {
128149
foreach ($input as $right) {
129-
foreach ($operands as $operand) {
130-
$line = prepareLine($left, $right, function() use ($left, $right, $operand) {
131-
return eval("return @(\$left $operand \$right);");
132-
}, $operand);
150+
foreach ($binaryOperators as $operator) {
151+
$line = prepareBinaryLine($left, $right, function() use ($left, $right, $operator) {
152+
return eval("return @(\$left $operator \$right);");
153+
}, $operator);
133154
fwrite($file, $line . "\n");
134155
}
135156
}
136157
}
158+
foreach ($input as $right) {
159+
foreach ($unaryOperators as $operator) {
160+
$line = prepareUnaryLine($right, function() use ($right, $operator) {
161+
return eval("return @($operator \$right);");
162+
}, $operator);
163+
fwrite($file, $line . "\n");
164+
}
165+
}
137166

138167
fclose($file);
139168

Zend/zend_compile.c

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7913,18 +7913,32 @@ static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode
79137913
}
79147914
/* }}} */
79157915

7916-
static inline void zend_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op) /* {{{ */
7916+
zend_bool zend_unary_op_produces_error(uint32_t opcode, zval *op)
79177917
{
7918+
if (opcode == ZEND_BW_NOT) {
7919+
return Z_TYPE_P(op) <= IS_TRUE || Z_TYPE_P(op) == IS_ARRAY;
7920+
}
7921+
7922+
return 0;
7923+
}
7924+
7925+
static inline zend_bool zend_try_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op) /* {{{ */
7926+
{
7927+
if (zend_unary_op_produces_error(opcode, op)) {
7928+
return 0;
7929+
}
7930+
79187931
unary_op_type fn = get_unary_op(opcode);
79197932
fn(result, op);
7933+
return 1;
79207934
}
79217935
/* }}} */
79227936

79237937
static inline zend_bool zend_try_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */
79247938
{
7925-
zval left;
7926-
ZVAL_LONG(&left, (kind == ZEND_AST_UNARY_PLUS) ? 1 : -1);
7927-
return zend_try_ct_eval_binary_op(result, ZEND_MUL, &left, op);
7939+
zval right;
7940+
ZVAL_LONG(&right, (kind == ZEND_AST_UNARY_PLUS) ? 1 : -1);
7941+
return zend_try_ct_eval_binary_op(result, ZEND_MUL, op, &right);
79287942
}
79297943
/* }}} */
79307944

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

8188-
if (expr_node.op_type == IS_CONST) {
8202+
if (expr_node.op_type == IS_CONST
8203+
&& zend_try_ct_eval_unary_op(&result->u.constant, opcode, &expr_node.u.constant)) {
81898204
result->op_type = IS_CONST;
8190-
zend_ct_eval_unary_op(&result->u.constant, opcode,
8191-
&expr_node.u.constant);
81928205
zval_ptr_dtor(&expr_node.u.constant);
81938206
return;
81948207
}
@@ -8200,8 +8213,7 @@ void zend_compile_unary_op(znode *result, zend_ast *ast) /* {{{ */
82008213
void zend_compile_unary_pm(znode *result, zend_ast *ast) /* {{{ */
82018214
{
82028215
zend_ast *expr_ast = ast->child[0];
8203-
znode expr_node;
8204-
znode lefthand_node;
8216+
znode expr_node, right_node;
82058217

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

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

8217-
lefthand_node.op_type = IS_CONST;
8218-
ZVAL_LONG(&lefthand_node.u.constant, (ast->kind == ZEND_AST_UNARY_PLUS) ? 1 : -1);
8219-
zend_emit_op_tmp(result, ZEND_MUL, &lefthand_node, &expr_node);
8229+
right_node.op_type = IS_CONST;
8230+
ZVAL_LONG(&right_node.u.constant, (ast->kind == ZEND_AST_UNARY_PLUS) ? 1 : -1);
8231+
zend_emit_op_tmp(result, ZEND_MUL, &expr_node, &right_node);
82208232
}
82218233
/* }}} */
82228234

@@ -9752,7 +9764,9 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
97529764
return;
97539765
}
97549766

9755-
zend_ct_eval_unary_op(&result, ast->attr, zend_ast_get_zval(ast->child[0]));
9767+
if (!zend_try_ct_eval_unary_op(&result, ast->attr, zend_ast_get_zval(ast->child[0]))) {
9768+
return;
9769+
}
97569770
break;
97579771
case ZEND_AST_UNARY_PLUS:
97589772
case ZEND_AST_UNARY_MINUS:

0 commit comments

Comments
 (0)