Commit bf4b9265442 for php.net

commit bf4b926544299d6650d7517c1927bf56c7d84ab0
Merge: 5ee37f68279 40c3e247f9b
Author: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date:   Thu Jul 30 09:47:46 2026 +0200

    Merge branch 'PHP-8.4' into PHP-8.5

    * PHP-8.4:
      CS
      Fix GH-22857: Function JIT emits wrong code for FETCH_OBJ_FUNC_ARG on a property hook (#22897)

diff --cc NEWS
index 28a4ef939ea,7f4d24f6b0a..3282ce1435b
--- a/NEWS
+++ b/NEWS
@@@ -11,12 -8,9 +11,14 @@@ PH
  - DOM:
    . Fixed bug GH-22825 (DOMElement::setAttribute() fails silently when the DTD
      declares a default value for the attribute). (iliaal)
 -  . Fixed bug GH-22447 (UAF at dom_objects_free_storage when setting an
 -    attribute node that collides by local name with a namespaced
 -    attribute). (David Carlier)
 +
 +- Opcache:
 +  . Fixed GH-22693 (DT_TEXTREL in JIT-generated TLS access on x86_64).
 +    (David Carlier)
 +  . Fixed bug GH-22763 (JIT fails to clear ZREG_TYPE_ONLY after setting reg).
 +    (Arnaud)
++  . Fixed bug GH-22857 (Function JIT emits wrong code for FETCH_OBJ_FUNC_ARG on
++    a property hook getter, losing register-held variables). (Zhao Hao)

  - MBString:
    . Fixed bug GH-22779 (mb_strrpos() returns the wrong position for a negative
diff --cc ext/opcache/jit/zend_jit.c
index 5de9c81484b,a958a2453c4..76510743d33
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@@ -2483,29 -2370,33 +2484,49 @@@ static int zend_jit(const zend_op_arra
  						 || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
  							break;
  						}
- 						if (!zend_jit_fetch_obj(&ctx, opline, op_array, ssa, ssa_op,
- 								op1_info, op1_addr, 0, ce, ce_is_instanceof, on_this, 0, 0, NULL,
- 								RES_REG_ADDR(), IS_UNKNOWN,
- 								zend_may_throw(opline, ssa_op, op_array, ssa))) {
- 							goto jit_failure;
+ 						if (opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) {
+ 							/* FETCH_OBJ_FUNC_ARG's by-value fetch dispatches into the
+ 							 * FETCH_OBJ_R handler, which may take the SIMPLE_GET hook fast
+ 							 * path and push a getter frame; by-ref dispatches into
+ 							 * FETCH_OBJ_W. The function JIT may keep values solely in
+ 							 * registers, so we must NOT exit to the VM (stale stack slots).
+ 							 * Inline the by-value path through zend_jit_fetch_obj, which runs
+ 							 * the hook getter inside a helper and keeps all registers live.
+ 							 * The by-ref path has no SIMPLE_GET fast path, so the generic
+ 							 * handler (a full C call, safe under register allocation) is used.
+ 							 * This mirrors the tracing JIT fix for GH-21006 (GH-21369); the
+ 							 * runtime by-ref check is required because the passing mode is
+ 							 * only known once the callee is resolved via namespace fallback.
+ 							 * See GH-22857. */
+ 							if (!zend_jit_fetch_obj_func_arg(jit, opline, op_array, ssa, ssa_op,
+ 									op1_info, op1_addr, ce, ce_is_instanceof, on_this, RES_REG_ADDR())) {
+ 								goto jit_failure;
+ 							}
+ 						} else {
+ 							if (!zend_jit_fetch_obj(&ctx, opline, op_array, ssa, ssa_op,
+ 									op1_info, op1_addr, 0, ce, ce_is_instanceof, on_this, 0, 0, NULL,
+ 									RES_REG_ADDR(), IS_UNKNOWN,
+ 									zend_may_throw(opline, ssa_op, op_array, ssa))) {
+ 								goto jit_failure;
+ 							}
  						}
  						goto done;
 +					case ZEND_FETCH_STATIC_PROP_R:
 +					case ZEND_FETCH_STATIC_PROP_IS:
 +					case ZEND_FETCH_STATIC_PROP_W:
 +					case ZEND_FETCH_STATIC_PROP_RW:
 +					case ZEND_FETCH_STATIC_PROP_UNSET:
 +						if (!(opline->op1_type == IS_CONST
 +						 && (opline->op2_type == IS_CONST
 +						  || (opline->op2_type == IS_UNUSED
 +						   && ((opline->op2.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF
 +						    || (opline->op2.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT))))) {
 +							break;
 +						}
 +						if (!zend_jit_fetch_static_prop(&ctx, opline, op_array)) {
 +							goto jit_failure;
 +						}
 +						goto done;
  					case ZEND_BIND_GLOBAL:
  						if (!ssa->ops || !ssa->var_info) {
  							op1_info = MAY_BE_ANY|MAY_BE_REF;