From 1fdcfa4ebe842531f2b403d45a92012c83125ba1 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 d79d0e4fe22..92b6027c4fd 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.1.27 +- 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 90704993bb2..fd5b7242ba6 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 530fd7d3e11..bc098148541 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -9755,6 +9755,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)) { @@ -9762,6 +9766,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) { @@ -17560,6 +17567,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)) { @@ -17567,6 +17578,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) { @@ -47008,6 +47022,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)) { @@ -47015,6 +47033,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) {