From b2d2005758a78f5033b04c4c8b39bfc8f75293c2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 23 Sep 2024 20:11:02 +0200 Subject: [PATCH 1/2] Fix GH-16009: Segmentation fault with frameless functions and undefined CVs The frameless function handlers do not update the op variables when handling the result is undefined. In this case this causes propagating an UNDEF value into a temporary, which results in an extra undefined variable warning for a temporary in this case. The original issue also reports a crash in some cases, which is also fixed by this patch. --- ext/opcache/jit/zend_jit_ir.c | 24 ++++++++++++++++++------ ext/opcache/tests/jit/gh16009.phpt | 22 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 ext/opcache/tests/jit/gh16009.phpt diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index f8a24c18c242a..1c75e07a7f4d7 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -17195,7 +17195,9 @@ static void jit_frameless_icall1(zend_jit_ctx *jit, const zend_op *opline, uint3 ir_ref op1_ref = jit_ZVAL_ADDR(jit, op1_addr); jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL); if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF)) { - zend_jit_zval_check_undef(jit, op1_ref, opline->op1.var, opline, 1); + op1_ref = zend_jit_zval_check_undef(jit, op1_ref, opline->op1.var, opline, 1); + op1_info |= MAY_BE_NULL; + op1_addr = ZEND_ADDR_REF_ZVAL(op1_ref); } if (op1_info & MAY_BE_REF) { op1_ref = jit_ZVAL_DEREF_ref(jit, op1_ref); @@ -17237,10 +17239,14 @@ static void jit_frameless_icall2(zend_jit_ctx *jit, const zend_op *opline, uint3 ir_ref op2_ref = jit_ZVAL_ADDR(jit, op2_addr); jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL); if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF)) { - zend_jit_zval_check_undef(jit, op1_ref, opline->op1.var, opline, 1); + op1_ref = zend_jit_zval_check_undef(jit, op1_ref, opline->op1.var, opline, 1); + op1_info |= MAY_BE_NULL; + op1_addr = ZEND_ADDR_REF_ZVAL(op1_ref); } if (opline->op2_type == IS_CV && (op2_info & MAY_BE_UNDEF)) { - zend_jit_zval_check_undef(jit, op2_ref, opline->op2.var, opline, 1); + op2_ref = zend_jit_zval_check_undef(jit, op2_ref, opline->op2.var, opline, 1); + op2_info |= MAY_BE_NULL; + op2_addr = ZEND_ADDR_REF_ZVAL(op2_ref); } if (op1_info & MAY_BE_REF) { op1_ref = jit_ZVAL_DEREF_ref(jit, op1_ref); @@ -17296,13 +17302,19 @@ static void jit_frameless_icall3(zend_jit_ctx *jit, const zend_op *opline, uint3 ir_ref op3_ref = jit_ZVAL_ADDR(jit, op3_addr); jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL); if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF)) { - zend_jit_zval_check_undef(jit, op1_ref, opline->op1.var, opline, 1); + op1_ref = zend_jit_zval_check_undef(jit, op1_ref, opline->op1.var, opline, 1); + op1_info |= MAY_BE_NULL; + op1_addr = ZEND_ADDR_REF_ZVAL(op1_ref); } if (opline->op2_type == IS_CV && (op2_info & MAY_BE_UNDEF)) { - zend_jit_zval_check_undef(jit, op2_ref, opline->op2.var, opline, 1); + op2_ref = zend_jit_zval_check_undef(jit, op2_ref, opline->op2.var, opline, 1); + op2_info |= MAY_BE_NULL; + op2_addr = ZEND_ADDR_REF_ZVAL(op2_ref); } if ((opline+1)->op1_type == IS_CV && (op1_data_info & MAY_BE_UNDEF)) { - zend_jit_zval_check_undef(jit, op3_ref, (opline+1)->op1.var, opline, 1); + op3_ref = zend_jit_zval_check_undef(jit, op3_ref, (opline+1)->op1.var, opline, 1); + op1_data_info |= MAY_BE_NULL; + op3_addr = ZEND_ADDR_REF_ZVAL(op3_ref); } if (op1_info & MAY_BE_REF) { op1_ref = jit_ZVAL_DEREF_ref(jit, op1_ref); diff --git a/ext/opcache/tests/jit/gh16009.phpt b/ext/opcache/tests/jit/gh16009.phpt new file mode 100644 index 0000000000000..6c1d6b6984d88 --- /dev/null +++ b/ext/opcache/tests/jit/gh16009.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-16009 (Segmentation fault with frameless functions and undefined CVs) +--EXTENSIONS-- +opcache +--INI-- +opcache.jit=1012 +--FILE-- + +--EXPECTF-- +Warning: Undefined variable $value in %s on line %d + +Fatal error: Uncaught TypeError: testMin2Second(): Return value must be of type int, null returned in %s:%d +Stack trace: +#0 %s(%d): testMin2Second() +#1 {main} + thrown in %s on line %d From d3f5c33ee57f4e9bb6c0684137db2396cd0bf6b0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:41:12 +0200 Subject: [PATCH 2/2] Remove MAY_BE_UNDEF flag --- ext/opcache/jit/zend_jit_ir.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 1c75e07a7f4d7..a5d35c69598d1 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -17196,6 +17196,7 @@ static void jit_frameless_icall1(zend_jit_ctx *jit, const zend_op *opline, uint3 jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL); if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF)) { op1_ref = zend_jit_zval_check_undef(jit, op1_ref, opline->op1.var, opline, 1); + op1_info &= ~MAY_BE_UNDEF; op1_info |= MAY_BE_NULL; op1_addr = ZEND_ADDR_REF_ZVAL(op1_ref); } @@ -17240,11 +17241,13 @@ static void jit_frameless_icall2(zend_jit_ctx *jit, const zend_op *opline, uint3 jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL); if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF)) { op1_ref = zend_jit_zval_check_undef(jit, op1_ref, opline->op1.var, opline, 1); + op1_info &= ~MAY_BE_UNDEF; op1_info |= MAY_BE_NULL; op1_addr = ZEND_ADDR_REF_ZVAL(op1_ref); } if (opline->op2_type == IS_CV && (op2_info & MAY_BE_UNDEF)) { op2_ref = zend_jit_zval_check_undef(jit, op2_ref, opline->op2.var, opline, 1); + op2_info &= ~MAY_BE_UNDEF; op2_info |= MAY_BE_NULL; op2_addr = ZEND_ADDR_REF_ZVAL(op2_ref); } @@ -17303,16 +17306,19 @@ static void jit_frameless_icall3(zend_jit_ctx *jit, const zend_op *opline, uint3 jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL); if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF)) { op1_ref = zend_jit_zval_check_undef(jit, op1_ref, opline->op1.var, opline, 1); + op1_info &= ~MAY_BE_UNDEF; op1_info |= MAY_BE_NULL; op1_addr = ZEND_ADDR_REF_ZVAL(op1_ref); } if (opline->op2_type == IS_CV && (op2_info & MAY_BE_UNDEF)) { op2_ref = zend_jit_zval_check_undef(jit, op2_ref, opline->op2.var, opline, 1); + op2_info &= ~MAY_BE_UNDEF; op2_info |= MAY_BE_NULL; op2_addr = ZEND_ADDR_REF_ZVAL(op2_ref); } if ((opline+1)->op1_type == IS_CV && (op1_data_info & MAY_BE_UNDEF)) { op3_ref = zend_jit_zval_check_undef(jit, op3_ref, (opline+1)->op1.var, opline, 1); + op1_data_info &= ~MAY_BE_UNDEF; op1_data_info |= MAY_BE_NULL; op3_addr = ZEND_ADDR_REF_ZVAL(op3_ref); }