diff --git a/Zend/zend.c b/Zend/zend.c index d2be69a75769..fa9e98ecc0d3 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -80,6 +80,8 @@ static char *zend_version_info; static uint32_t zend_version_info_length; #define ZEND_CORE_VERSION_INFO "Zend Engine v" ZEND_VERSION ", Copyright (c) Zend Technologies\n" #define PRINT_ZVAL_INDENT 4 +#define ZEND_ERR_BUF_SIZE(capacity) \ + (XtOffsetOf(struct zend_err_buf, buf) + sizeof(zend_error_info *) * (capacity)) /* true multithread-shared globals */ ZEND_API zend_class_entry *zend_standard_class_def = NULL; @@ -642,6 +644,13 @@ static FILE *zend_fopen_wrapper(zend_string *filename, zend_string **opened_path } /* }}} */ +static void zend_init_errors_buf(zend_executor_globals *eg) +{ + eg->errors = pemalloc(ZEND_ERR_BUF_SIZE(2), true); + eg->errors->capacity = 2; + eg->errors->size = 0; +} + #ifdef ZTS static bool short_tags_default = true; static uint32_t compiler_options_default = ZEND_COMPILE_DEFAULT; @@ -833,8 +842,7 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{ #endif executor_globals->flags = EG_FLAGS_INITIAL; executor_globals->record_errors = false; - executor_globals->num_errors = 0; - executor_globals->errors = NULL; + zend_init_errors_buf(executor_globals); executor_globals->filename_override = NULL; executor_globals->lineno_override = -1; #ifdef ZEND_CHECK_STACK_LIMIT @@ -869,6 +877,8 @@ static void executor_globals_dtor(zend_executor_globals *executor_globals) /* {{ zend_hash_destroy(executor_globals->zend_constants); free(executor_globals->zend_constants); } + + pefree(executor_globals->errors, true); } /* }}} */ @@ -1065,6 +1075,7 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */ zend_init_rsrc_plist(); zend_init_exception_op(); zend_init_call_trampoline_op(); + zend_init_errors_buf(&executor_globals); #endif zend_ini_startup(); @@ -1147,6 +1158,7 @@ zend_result zend_post_startup(void) /* {{{ */ free(EG(zend_constants)); EG(zend_constants) = NULL; + pefree(executor_globals->errors, true); executor_globals_ctor(executor_globals); global_persistent_list = &EG(persistent_list); zend_copy_ini_directives(); @@ -1224,6 +1236,7 @@ void zend_shutdown(void) /* {{{ */ pefree(CG(internal_run_time_cache), 1); CG(internal_run_time_cache) = NULL; } + pefree(EG(errors), true); #endif zend_map_ptr_static_last = 0; zend_map_ptr_static_size = 0; @@ -1446,8 +1459,8 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_stack delayed_oplines_stack; int type = orig_type & E_ALL; bool orig_record_errors; - uint32_t orig_num_errors; - zend_error_info **orig_errors; + uint32_t orig_num_errors = 0; + uint32_t orig_cap_errors = 0; zend_result res; /* If we're executing a function during SCCP, count any warnings that may be emitted, @@ -1459,11 +1472,11 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( } /* Emit any delayed error before handling fatal error */ - if ((type & E_FATAL_ERRORS) && !(type & E_DONT_BAIL) && EG(num_errors)) { - uint32_t num_errors = EG(num_errors); - zend_error_info **errors = EG(errors); - EG(num_errors) = 0; - EG(errors) = NULL; + if ((type & E_FATAL_ERRORS) && !(type & E_DONT_BAIL) && EG(errors->size)) { + uint32_t num_errors = EG(errors->size); + uint32_t cap_errors = EG(errors->capacity); + zend_error_info **errors = EG(errors->buf); + EG(errors->size) = 0; bool orig_record_errors = EG(record_errors); EG(record_errors) = false; @@ -1477,8 +1490,8 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( EG(user_error_handler_error_reporting) = orig_user_error_handler_error_reporting; EG(record_errors) = orig_record_errors; - EG(num_errors) = num_errors; - EG(errors) = errors; + EG(errors->size) = num_errors; + EG(errors->capacity) = cap_errors; } if (EG(record_errors)) { @@ -1487,12 +1500,14 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( info->lineno = error_lineno; info->filename = zend_string_copy(error_filename); info->message = zend_string_copy(message); - - /* This is very inefficient for a large number of errors. - * Use pow2 realloc if it becomes a problem. */ - EG(num_errors)++; - EG(errors) = erealloc(EG(errors), sizeof(zend_error_info*) * EG(num_errors)); - EG(errors)[EG(num_errors)-1] = info; + EG(errors->size)++; + if (EG(errors->size) >= EG(errors->capacity)) { + // not sure we can get high number of errors so safe `might be` over cautious here + uint32_t capacity = EG(errors->capacity) + (EG(errors->capacity) >> 1); + EG(errors) = safe_perealloc(EG(errors), sizeof(zend_error_info *), capacity, XtOffsetOf(struct zend_err_buf, buf), true); + EG(errors->capacity) = capacity; + } + EG(errors->buf)[EG(errors->size)-1] = info; /* Do not process non-fatal recorded error */ if (!(type & E_FATAL_ERRORS) || (type & E_DONT_BAIL)) { @@ -1575,17 +1590,18 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( } orig_record_errors = EG(record_errors); - orig_num_errors = EG(num_errors); - orig_errors = EG(errors); EG(record_errors) = false; - EG(num_errors) = 0; - EG(errors) = NULL; + + orig_num_errors = EG(errors->size); + orig_cap_errors = EG(errors->capacity); + EG(errors->size) = 0; res = call_user_function(CG(function_table), NULL, &orig_user_error_handler, &retval, 4, params); EG(record_errors) = orig_record_errors; - EG(num_errors) = orig_num_errors; - EG(errors) = orig_errors; + + EG(errors->capacity) = orig_cap_errors; + EG(errors->size) = orig_num_errors; if (res == SUCCESS) { if (Z_TYPE(retval) != IS_UNDEF) { @@ -1780,8 +1796,7 @@ ZEND_API void zend_begin_record_errors(void) { ZEND_ASSERT(!EG(record_errors) && "Error recording already enabled"); EG(record_errors) = true; - EG(num_errors) = 0; - EG(errors) = NULL; + EG(errors->size) = 0; } ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors) @@ -1795,24 +1810,22 @@ ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info ZEND_API void zend_emit_recorded_errors(void) { EG(record_errors) = false; - zend_emit_recorded_errors_ex(EG(num_errors), EG(errors)); + zend_emit_recorded_errors_ex(EG(errors->size), EG(errors->buf)); } ZEND_API void zend_free_recorded_errors(void) { - if (!EG(num_errors)) { + if (!EG(errors->size)) { return; } - for (uint32_t i = 0; i < EG(num_errors); i++) { - zend_error_info *info = EG(errors)[i]; + for (uint32_t i = 0; i < EG(errors->size); i++) { + zend_error_info *info = EG(errors->buf)[i]; zend_string_release(info->filename); zend_string_release(info->message); - efree(info); + efree_size(info, sizeof(zend_error_info)); } - efree(EG(errors)); - EG(errors) = NULL; - EG(num_errors) = 0; + EG(errors->size) = 0; } ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format, ...) /* {{{ */ @@ -2020,12 +2033,12 @@ ZEND_API zend_result zend_execute_scripts(int type, zval *retval, int file_count } /* }}} */ -#define COMPILED_STRING_DESCRIPTION_FORMAT "%s(%d) : %s" +#define COMPILED_STRING_DESCRIPTION_FORMAT "%s(%u) : %s" ZEND_API char *zend_make_compiled_string_description(const char *name) /* {{{ */ { const char *cur_filename; - int cur_lineno; + uint32_t cur_lineno; char *compiled_string_description; if (zend_is_compiling()) { diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 69337e27fd53..2faf33deac2c 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -192,8 +192,7 @@ void init_executor(void) /* {{{ */ EG(get_gc_buffer).start = EG(get_gc_buffer).end = EG(get_gc_buffer).cur = NULL; EG(record_errors) = false; - EG(num_errors) = 0; - EG(errors) = NULL; + EG(errors->size) = 0; EG(filename_override) = NULL; EG(lineno_override) = -1; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index f09b81acb315..eaa5791723ab 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -74,6 +74,7 @@ typedef struct _zend_vm_stack *zend_vm_stack; typedef struct _zend_ini_entry zend_ini_entry; typedef struct _zend_fiber_context zend_fiber_context; typedef struct _zend_fiber zend_fiber; +typedef struct _zend_error_info zend_error_info; typedef enum { ZEND_MEMOIZE_NONE, @@ -81,6 +82,12 @@ typedef enum { ZEND_MEMOIZE_FETCH, } zend_memoize_mode; +struct zend_err_buf { + uint32_t size; + uint32_t capacity; + zend_error_info *buf[1]; +}; + struct _zend_compiler_globals { zend_stack loop_var_stack; @@ -298,8 +305,6 @@ struct _zend_executor_globals { * and their processing is delayed until zend_emit_recorded_errors() * is called or a fatal diagnostic is emitted. */ bool record_errors; - uint32_t num_errors; - zend_error_info **errors; /* Override filename or line number of thrown errors and exceptions */ zend_string *filename_override; @@ -322,6 +327,7 @@ struct _zend_executor_globals { HashTable callable_convert_cache; void *reserved[ZEND_MAX_RESERVED_RESOURCES]; + struct zend_err_buf *errors; }; #define EG_FLAGS_INITIAL (0) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index f4134212bc4e..6de3a3a5db09 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -1974,8 +1974,8 @@ static zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int if (persistent_script) { if (ZCG(accel_directives).record_warnings) { - persistent_script->num_warnings = EG(num_errors); - persistent_script->warnings = EG(errors); + persistent_script->num_warnings = EG(errors->size); + persistent_script->warnings = EG(errors->buf); } from_memory = false; @@ -2199,8 +2199,8 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) from_shared_memory = false; if (persistent_script) { if (ZCG(accel_directives).record_warnings) { - persistent_script->num_warnings = EG(num_errors); - persistent_script->warnings = EG(errors); + persistent_script->num_warnings = EG(errors->size); + persistent_script->warnings = EG(errors->buf); } /* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */ @@ -2428,7 +2428,7 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, } ZCG(current_persistent_script) = &dummy; zend_persist_class_entry_calc(ce); - zend_persist_warnings_calc(EG(num_errors), EG(errors)); + zend_persist_warnings_calc(EG(errors->size), EG(errors->buf)); size = dummy.size; zend_shared_alloc_clear_xlat_table(); @@ -2498,8 +2498,8 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, JIT_G(on) = jit_on_old; #endif - entry->num_warnings = EG(num_errors); - entry->warnings = zend_persist_warnings(EG(num_errors), EG(errors)); + entry->num_warnings = EG(errors->size); + entry->warnings = zend_persist_warnings(EG(errors->size), EG(errors->buf)); entry->next = proto->inheritance_cache; proto->inheritance_cache = entry; @@ -4133,9 +4133,9 @@ static void preload_link(void) /* Remember the last error. */ zend_error_cb = orig_error_cb; EG(record_errors) = false; - ZEND_ASSERT(EG(num_errors) > 0); - zend_hash_update_ptr(&errors, key, EG(errors)[EG(num_errors)-1]); - EG(num_errors)--; + ZEND_ASSERT(EG(errors->size) > 0); + zend_hash_update_ptr(&errors, key, EG(errors->buf)[EG(errors->size)-1]); + EG(errors->size)--; } zend_end_try(); CG(in_compilation) = false; CG(compiled_filename) = NULL;