Commit 269f7b762b5 for php
commit 269f7b762b5938915a6a2792a8b9b0d81edc664a
Author: ndossche <7771979+ndossche@users.noreply.github.com>
Date: Sat Oct 10 15:31:32 2026 +0200
Fix exception checks due to NAN warnings and object empty()'s
The empty() check for objects must be backported to 8.4, the NAN problem
is new since 320fe2975b.
Closes GH-24241.
diff --git a/NEWS b/NEWS
index 34f540d39b4..262f3f4fd7a 100644
--- a/NEWS
+++ b/NEWS
@@ -30,6 +30,7 @@ PHP NEWS
. Fix type inference of ADD_ARRAY_UNPACK with integer keys. (ndossche)
. Fix too wide type inference for ASSIGN_DIM_OP. (ndossche)
. Fix OSS-Fuzz #568005340 (FETCH_DIM_FUNC_ARG partial conversion). (ndossche)
+ . Fix exception checks due to NAN warnings and object empty()'s. (ndossche)
- Standard:
. Fixed password_get_info() and password_needs_rehash() accepting malformed
diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c
index b862fe48361..af31ec757be 100644
--- a/Zend/Optimizer/zend_inference.c
+++ b/Zend/Optimizer/zend_inference.c
@@ -5030,7 +5030,6 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
case ZEND_ISSET_ISEMPTY_VAR:
- case ZEND_ISSET_ISEMPTY_CV:
case ZEND_FUNC_NUM_ARGS:
case ZEND_FUNC_GET_ARGS:
case ZEND_COPY_TMP:
@@ -5118,7 +5117,14 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
- return (t1 & MAY_BE_OBJECT);
+ /* NAN cast to bool will warn */
+ return (t1 & (MAY_BE_OBJECT|MAY_BE_DOUBLE));
+ case ZEND_ISSET_ISEMPTY_CV:
+ if (!(opline->extended_value & ZEND_ISEMPTY)) {
+ return 0;
+ }
+ /* empty() casts to bool: objects may have a cast handler, and NAN will warn */
+ return (t1 & (MAY_BE_OBJECT|MAY_BE_DOUBLE));
case ZEND_BOOL:
case ZEND_BOOL_NOT:
/* NAN Cast to bool will warn, but if we have a range it is fine */
diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c
index ec745d0613a..471cf55c654 100644
--- a/ext/opcache/jit/zend_jit_ir.c
+++ b/ext/opcache/jit/zend_jit_ir.c
@@ -7753,6 +7753,8 @@ static int zend_jit_bool_jmpznz(zend_jit_ctx *jit, const zend_op *opline, uint32
ir_IF_TRUE_cold(if_val);
jit_SET_EX_OPLINE(jit, opline);
ir_CALL(IR_VOID, ir_CONST_FC_FUNC(zend_jit_nan_coerced_to_type_warning));
+ /* The warning may be turned into an exception by an error handler */
+ zend_jit_check_exception_undef_result(jit, opline);
ir_MERGE_WITH_EMPTY_FALSE(if_val);
ref = ir_NE(dval, ir_CONST_DOUBLE(0.0));
diff --git a/ext/opcache/tests/jit/nan_to_bool_exception.phpt b/ext/opcache/tests/jit/nan_to_bool_exception.phpt
new file mode 100644
index 00000000000..c612c008f31
--- /dev/null
+++ b/ext/opcache/tests/jit/nan_to_bool_exception.phpt
@@ -0,0 +1,46 @@
+--TEST--
+JIT: NAN to bool coercion warning promoted to exception
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit=1205
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+set_error_handler(function ($no, $str) {
+ throw new Exception($str);
+});
+
+function jmpz(float $d) {
+ if ($d) {
+ echo "side effect\n";
+ }
+ echo "side effect\n";
+}
+
+function jmp_set(float $d) {
+ $r = $d ?: 1;
+ echo "side effect\n";
+ return $r;
+}
+
+function isempty(float $d) {
+ $r = empty($d);
+ echo "side effect\n";
+ return $r;
+}
+
+foreach (["jmpz", "jmp_set", "isempty"] as $f) {
+ try {
+ $f(NAN);
+ } catch (Exception $e) {
+ echo "$f: ", $e->getMessage(), "\n";
+ }
+}
+?>
+--EXPECT--
+jmpz: unexpected NAN value was coerced to bool
+jmp_set: unexpected NAN value was coerced to bool
+isempty: unexpected NAN value was coerced to bool