From 5518165499498be880ed27b178df4d428c61a951 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 30 Oct 2025 14:04:09 +0100 Subject: [PATCH 1/2] Fix EG(current_execute_data) introduced in 1292037 Fixes OSS-Fuzz #456317305 --- Zend/tests/oss_fuzz_456317305.phpt | 25 +++++++++++++++++++++++++ Zend/zend_generators.c | 12 ++++++++---- Zend/zend_objects.c | 21 ++++++++++++--------- 3 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 Zend/tests/oss_fuzz_456317305.phpt diff --git a/Zend/tests/oss_fuzz_456317305.phpt b/Zend/tests/oss_fuzz_456317305.phpt new file mode 100644 index 0000000000000..37e7c59e46515 --- /dev/null +++ b/Zend/tests/oss_fuzz_456317305.phpt @@ -0,0 +1,25 @@ +--TEST-- +OSS-Fuzz #456317305: EG(current_execute_data) NULL pointer violation +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 [internal function]: C->__destruct() +#1 {main} + thrown in %s on line %d diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 84b40cfdc21a3..5ba215f788ce9 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -321,9 +321,11 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ zend_object *old_exception = NULL; const zend_op *old_opline_before_exception = NULL; if (EG(exception)) { - EG(current_execute_data)->opline = EG(opline_before_exception); + if (EG(current_execute_data)) { + EG(current_execute_data)->opline = EG(opline_before_exception); + old_opline_before_exception = EG(opline_before_exception); + } old_exception = EG(exception); - old_opline_before_exception = EG(opline_before_exception); EG(exception) = NULL; } @@ -335,8 +337,10 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ zend_generator_resume(generator); if (old_exception) { - EG(current_execute_data)->opline = EG(exception_op); - EG(opline_before_exception) = old_opline_before_exception; + if (EG(current_execute_data)) { + EG(current_execute_data)->opline = EG(exception_op); + EG(opline_before_exception) = old_opline_before_exception; + } if (EG(exception)) { zend_exception_set_previous(EG(exception), old_exception); } else { diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 30ea22c8de44f..8d9bbcc6aa748 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -100,7 +100,7 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) if (destructor) { zend_object *old_exception; - const zend_op *old_opline_before_exception; + const zend_op *old_opline_before_exception = NULL; if (destructor->op_array.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) { if (destructor->op_array.fn_flags & ZEND_ACC_PRIVATE) { @@ -159,14 +159,15 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) if (EG(exception) == object) { zend_error_noreturn(E_CORE_ERROR, "Attempt to destruct pending exception"); } else { - if (EG(current_execute_data) - && EG(current_execute_data)->func - && ZEND_USER_CODE(EG(current_execute_data)->func->common.type)) { - zend_rethrow_exception(EG(current_execute_data)); + if (EG(current_execute_data)) { + if (EG(current_execute_data)->func + && ZEND_USER_CODE(EG(current_execute_data)->func->common.type)) { + zend_rethrow_exception(EG(current_execute_data)); + } + EG(current_execute_data)->opline = EG(opline_before_exception); + old_opline_before_exception = EG(opline_before_exception); } - EG(current_execute_data)->opline = EG(opline_before_exception); old_exception = EG(exception); - old_opline_before_exception = EG(opline_before_exception); EG(exception) = NULL; } } @@ -174,8 +175,10 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) zend_call_known_instance_method_with_0_params(destructor, object, NULL); if (old_exception) { - EG(current_execute_data)->opline = EG(exception_op); - EG(opline_before_exception) = old_opline_before_exception; + if (EG(current_execute_data)) { + EG(current_execute_data)->opline = EG(exception_op); + EG(opline_before_exception) = old_opline_before_exception; + } if (EG(exception)) { zend_exception_set_previous(EG(exception), old_exception); } else { From 50c7f498b9a616704faeb48123182eda5fc5e896 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 29 Oct 2025 15:14:45 +0100 Subject: [PATCH 2/2] Reset Z_EXTRA_P(op2) of ZEND_INIT_FCALL for opcache file cache The offset becomes stale if the environment changes. We're currently relying on other factors in the environment staying constant, e.g. send types. But this seems to be the worst offender. Partially addresses GH-17733 Closes GH-20328 --- NEWS | 2 ++ ext/opcache/zend_file_cache.c | 21 ++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index a55358c265d1d..882a17d2ed304 100644 --- a/NEWS +++ b/NEWS @@ -55,6 +55,8 @@ PHP NEWS . Fixed bug GH-19875 (JIT 1205 segfault on large file compiled in subprocess). (Arnaud) . Fixed bug GH-20012 (heap buffer overflow in jit). (Arnaud) + . Partially fixed bug GH-17733 (Avoid calling wrong function when reusing file + caches across differing environments). (ilutov) - PgSql: . Fix memory leak when first string conversion fails. (nielsdos) diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 736d644516a04..36ab3f6c9ae9b 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -535,13 +535,32 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra } if (opline->op2_type == IS_CONST) { SERIALIZE_PTR(opline->op2.zv); + + /* See GH-17733. Reset Z_EXTRA_P(op2) of ZEND_INIT_FCALL, which + * is an offset into the global function table, to avoid calling + * incorrect functions when environment changes. This, and the + * equivalent code below, can be removed once proper system ID + * validation is implemented. */ + if (opline->opcode == ZEND_INIT_FCALL) { + zval *op2 = opline->op2.zv; + UNSERIALIZE_PTR(op2); + Z_EXTRA_P(op2) = 0; + ZEND_VM_SET_OPCODE_HANDLER(opline); + } } #else if (opline->op1_type == IS_CONST) { opline->op1.constant = RT_CONSTANT(opline, opline->op1) - literals; } if (opline->op2_type == IS_CONST) { - opline->op2.constant = RT_CONSTANT(opline, opline->op2) - literals; + zval *op2 = RT_CONSTANT(opline, opline->op2); + opline->op2.constant = op2 - literals; + + /* See GH-17733 and comment above. */ + if (opline->opcode == ZEND_INIT_FCALL) { + Z_EXTRA_P(op2) = 0; + ZEND_VM_SET_OPCODE_HANDLER(opline); + } } #endif #if ZEND_USE_ABS_JMP_ADDR