From ea52706a2a441db6e5dd024fe02fdaaa5de21b12 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 20 Nov 2023 11:56:06 +0100 Subject: [PATCH] Fix use-after-free of name in var-var with malicious error handler Fixes oss-fuzz #54325 Closes GH-12732 --- NEWS | 4 ++++ Zend/tests/oss_fuzz_54325.phpt | 19 +++++++++++++++++++ Zend/zend_vm_def.h | 7 +++++++ Zend/zend_vm_execute.h | 21 +++++++++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 Zend/tests/oss_fuzz_54325.phpt diff --git a/NEWS b/NEWS index 3bf074b1e1b..5558c0661f4 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.2.14 +- Core: + . Fixed oss-fuzz #54325 (Use-after-free of name in var-var with malicious + error handler). (ilutov) + - DOM: . Fixed bug GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix). (nielsdos) diff --git a/Zend/tests/oss_fuzz_54325.phpt b/Zend/tests/oss_fuzz_54325.phpt new file mode 100644 index 00000000000..d998acf1ffe --- /dev/null +++ b/Zend/tests/oss_fuzz_54325.phpt @@ -0,0 +1,19 @@ +--TEST-- +oss-fuzz #54325: Fix use-after-free of name in var-var with malicious error handler +--FILE-- + +--EXPECT-- +string(23) "Undefined variable $oof" +object(stdClass)#2 (0) { +} diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 8ad2c6116d8..4ea88e56fa2 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1748,6 +1748,10 @@ ZEND_VM_C_LABEL(fetch_this): } else if (type == BP_VAR_IS || type == BP_VAR_UNSET) { retval = &EG(uninitialized_zval); } else { + if (OP1_TYPE == IS_CV) { + /* Keep name alive in case an error handler tries to free it. */ + zend_string_addref(name); + } zend_error(E_WARNING, "Undefined %svariable $%s", (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); if (type == BP_VAR_RW && !EG(exception)) { @@ -1755,6 +1759,9 @@ ZEND_VM_C_LABEL(fetch_this): } else { retval = &EG(uninitialized_zval); } + if (OP1_TYPE == IS_CV) { + zend_string_release(name); + } } /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ } else if (Z_TYPE_P(retval) == IS_INDIRECT) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 22e9f5b62a8..447d43145c5 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -9859,6 +9859,10 @@ fetch_this: } else if (type == BP_VAR_IS || type == BP_VAR_UNSET) { retval = &EG(uninitialized_zval); } else { + if (IS_CONST == IS_CV) { + /* Keep name alive in case an error handler tries to free it. */ + zend_string_addref(name); + } zend_error(E_WARNING, "Undefined %svariable $%s", (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); if (type == BP_VAR_RW && !EG(exception)) { @@ -9866,6 +9870,9 @@ fetch_this: } else { retval = &EG(uninitialized_zval); } + if (IS_CONST == IS_CV) { + zend_string_release(name); + } } /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ } else if (Z_TYPE_P(retval) == IS_INDIRECT) { @@ -17667,6 +17674,10 @@ fetch_this: } else if (type == BP_VAR_IS || type == BP_VAR_UNSET) { retval = &EG(uninitialized_zval); } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV) { + /* Keep name alive in case an error handler tries to free it. */ + zend_string_addref(name); + } zend_error(E_WARNING, "Undefined %svariable $%s", (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); if (type == BP_VAR_RW && !EG(exception)) { @@ -17674,6 +17685,9 @@ fetch_this: } else { retval = &EG(uninitialized_zval); } + if ((IS_TMP_VAR|IS_VAR) == IS_CV) { + zend_string_release(name); + } } /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ } else if (Z_TYPE_P(retval) == IS_INDIRECT) { @@ -47240,6 +47254,10 @@ fetch_this: } else if (type == BP_VAR_IS || type == BP_VAR_UNSET) { retval = &EG(uninitialized_zval); } else { + if (IS_CV == IS_CV) { + /* Keep name alive in case an error handler tries to free it. */ + zend_string_addref(name); + } zend_error(E_WARNING, "Undefined %svariable $%s", (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); if (type == BP_VAR_RW && !EG(exception)) { @@ -47247,6 +47265,9 @@ fetch_this: } else { retval = &EG(uninitialized_zval); } + if (IS_CV == IS_CV) { + zend_string_release(name); + } } /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ } else if (Z_TYPE_P(retval) == IS_INDIRECT) {