From e7a61cfc43a926828d8da4ea8a94fe043d706be8 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 3 Apr 2026 11:27:52 -0400 Subject: [PATCH] Fix GH-19200: replace unchecked realloc/malloc with perealloc/pemalloc Raw realloc() returns NULL on allocation failure, losing the original pointer and causing a crash on the next dereference. pemalloc/perealloc with persistent=true wrap the system allocator but call zend_out_of_memory() on failure, giving a clean exit instead of an undefined crash. Converts all V701 locations from the PVS-Studio report and unchecked malloc calls in zend_register_functions() (GH-17013). Skips zend_alloc.c (already handled) and IR JIT code (third-party). The zend_inheritance.c changes also simplify the realloc/erealloc branch into a single perealloc() call, matching the existing pattern at zend_implement_stringable(). Fixes GH-19200 Closes GH-17013 --- Zend/zend.c | 2 +- Zend/zend_API.c | 20 ++++++++++---------- Zend/zend_inheritance.c | 12 ++---------- ext/opcache/zend_accelerator_blacklist.c | 2 +- main/network.c | 12 ++++++------ main/php_ini.c | 2 +- main/php_ini_builder.h | 2 +- sapi/phpdbg/phpdbg.c | 4 ++-- sapi/phpdbg/phpdbg_prompt.c | 4 ++-- 9 files changed, 26 insertions(+), 34 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index f4236053af3d..f83389a96a6e 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1303,7 +1303,7 @@ ZEND_API void zend_append_version_info(const zend_extension *extension) /* {{{ * snprintf(new_info, new_info_length, " with %s v%s, %s, by %s\n", extension->name, extension->version, extension->copyright, extension->author); - zend_version_info = (char *) realloc(zend_version_info, zend_version_info_length+new_info_length + 1); + zend_version_info = (char *) perealloc(zend_version_info, zend_version_info_length+new_info_length + 1, true); strncat(zend_version_info, new_info, new_info_length); zend_version_info_length += new_info_length; free(new_info); diff --git a/Zend/zend_API.c b/Zend/zend_API.c index c04217983b5d..ccb770a9963b 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2504,19 +2504,19 @@ ZEND_API void zend_collect_module_handlers(void) /* {{{ */ dl_loaded_count++; } } ZEND_HASH_FOREACH_END(); - module_request_startup_handlers = (zend_module_entry**)realloc( + module_request_startup_handlers = (zend_module_entry**)perealloc( module_request_startup_handlers, sizeof(zend_module_entry*) * (startup_count + 1 + shutdown_count + 1 + - post_deactivate_count + 1)); + post_deactivate_count + 1), true); module_request_startup_handlers[startup_count] = NULL; module_request_shutdown_handlers = module_request_startup_handlers + startup_count + 1; module_request_shutdown_handlers[shutdown_count] = NULL; module_post_deactivate_handlers = module_request_shutdown_handlers + shutdown_count + 1; module_post_deactivate_handlers[post_deactivate_count] = NULL; /* Cannot reuse module_request_startup_handlers because it is freed in zend_destroy_modules, which happens before zend_unload_modules. */ - modules_dl_loaded = realloc(modules_dl_loaded, sizeof(zend_module_entry*) * (dl_loaded_count + 1)); + modules_dl_loaded = perealloc(modules_dl_loaded, sizeof(zend_module_entry*) * (dl_loaded_count + 1), true); modules_dl_loaded[dl_loaded_count] = NULL; startup_count = 0; @@ -2543,10 +2543,10 @@ ZEND_API void zend_collect_module_handlers(void) /* {{{ */ } } ZEND_HASH_FOREACH_END(); - class_cleanup_handlers = (zend_class_entry**)realloc( + class_cleanup_handlers = (zend_class_entry**)perealloc( class_cleanup_handlers, sizeof(zend_class_entry*) * - (class_count + 1)); + (class_count + 1), true); class_cleanup_handlers[class_count] = NULL; if (class_count) { @@ -3143,7 +3143,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend } lowercase_name = zend_string_tolower_ex(internal_function->function_name, type == MODULE_PERSISTENT); lowercase_name = zend_new_interned_string(lowercase_name); - reg_function = malloc(sizeof(zend_internal_function)); + reg_function = pemalloc(sizeof(zend_internal_function), true); memcpy(reg_function, &function, sizeof(zend_internal_function)); if (zend_hash_add_ptr(target_function_table, lowercase_name, reg_function) == NULL) { unload=1; @@ -3161,8 +3161,8 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend zend_flf_capacity *= 2; } /* +1 for NULL terminator */ - zend_flf_handlers = realloc(zend_flf_handlers, (zend_flf_capacity + 1) * sizeof(void *)); - zend_flf_functions = realloc(zend_flf_functions, (zend_flf_capacity + 1) * sizeof(zend_function *)); + zend_flf_handlers = perealloc(zend_flf_handlers, (zend_flf_capacity + 1) * sizeof(void *), true); + zend_flf_functions = perealloc(zend_flf_functions, (zend_flf_capacity + 1) * sizeof(zend_function *), true); } zend_flf_handlers[zend_flf_count] = flf_info->handler; zend_flf_functions[zend_flf_count] = (zend_function *)reg_function; @@ -3208,7 +3208,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend /* Treat return type as an extra argument */ num_args++; - new_arg_info = malloc(sizeof(zend_arg_info) * num_args); + new_arg_info = pemalloc(sizeof(zend_arg_info) * num_args, true); reg_function->arg_info = new_arg_info + 1; for (i = 0; i < num_args; i++) { zend_convert_internal_arg_info(&new_arg_info[i], &arg_info[i], @@ -3493,7 +3493,7 @@ ZEND_API int zend_next_free_module(void) /* {{{ */ static zend_class_entry *do_register_internal_class(const zend_class_entry *orig_class_entry, uint32_t ce_flags) /* {{{ */ { - zend_class_entry *class_entry = malloc(sizeof(zend_class_entry)); + zend_class_entry *class_entry = pemalloc(sizeof(zend_class_entry), true); zend_string *lowercase_name; *class_entry = *orig_class_entry; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index bf4c84404a67..d70d08f5f1c4 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1595,11 +1595,7 @@ static void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_en ce_num = ce->num_interfaces; - if (ce->type == ZEND_INTERNAL_CLASS) { - ce->interfaces = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (ce_num + if_num)); - } else { - ce->interfaces = (zend_class_entry **) erealloc(ce->interfaces, sizeof(zend_class_entry *) * (ce_num + if_num)); - } + ce->interfaces = (zend_class_entry **) perealloc(ce->interfaces, sizeof(zend_class_entry *) * (ce_num + if_num), ce->type == ZEND_INTERNAL_CLASS); /* Inherit the interfaces, only if they're not already inherited by the class */ while (if_num--) { @@ -2234,11 +2230,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry } ZEND_HASH_FOREACH_END(); } else { if (ce->num_interfaces >= current_iface_num) { - if (ce->type == ZEND_INTERNAL_CLASS) { - ce->interfaces = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num)); - } else { - ce->interfaces = (zend_class_entry **) erealloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num)); - } + ce->interfaces = (zend_class_entry **) perealloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num), ce->type == ZEND_INTERNAL_CLASS); } ce->interfaces[ce->num_interfaces++] = iface; diff --git a/ext/opcache/zend_accelerator_blacklist.c b/ext/opcache/zend_accelerator_blacklist.c index 56a4ceb4dc4d..ed03a82e9f3f 100644 --- a/ext/opcache/zend_accelerator_blacklist.c +++ b/ext/opcache/zend_accelerator_blacklist.c @@ -225,7 +225,7 @@ static inline void zend_accel_blacklist_allocate(zend_blacklist *blacklist) { if (blacklist->pos == blacklist->size) { blacklist->size += ZEND_BLACKLIST_BLOCK_SIZE; - blacklist->entries = (zend_blacklist_entry *) realloc(blacklist->entries, sizeof(zend_blacklist_entry)*blacklist->size); + blacklist->entries = (zend_blacklist_entry *) perealloc(blacklist->entries, sizeof(zend_blacklist_entry)*blacklist->size, true); } } diff --git a/main/network.c b/main/network.c index f652cf555ffb..90d1716b5582 100644 --- a/main/network.c +++ b/main/network.c @@ -1459,7 +1459,7 @@ static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf if (*hstbuflen == 0) { *hstbuflen = 1024; - *tmphstbuf = (char *)malloc (*hstbuflen); + *tmphstbuf = (char *)pemalloc(*hstbuflen, true); } while (( res = @@ -1467,7 +1467,7 @@ static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf && (errno == ERANGE)) { /* Enlarge the buffer. */ *hstbuflen *= 2; - *tmphstbuf = (char *)realloc (*tmphstbuf,*hstbuflen); + *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, true); } if (res != 0) { @@ -1485,7 +1485,7 @@ static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf if (*hstbuflen == 0) { *hstbuflen = 1024; - *tmphstbuf = (char *)malloc (*hstbuflen); + *tmphstbuf = (char *)pemalloc(*hstbuflen, true); } while ((NULL == ( hp = @@ -1493,7 +1493,7 @@ static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf && (errno == ERANGE)) { /* Enlarge the buffer. */ *hstbuflen *= 2; - *tmphstbuf = (char *)realloc (*tmphstbuf,*hstbuflen); + *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, true); } return hp; } @@ -1503,11 +1503,11 @@ static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf { if (*hstbuflen == 0) { *hstbuflen = sizeof(struct hostent_data); - *tmphstbuf = (char *)malloc (*hstbuflen); + *tmphstbuf = (char *)pemalloc(*hstbuflen, true); } else { if (*hstbuflen < sizeof(struct hostent_data)) { *hstbuflen = sizeof(struct hostent_data); - *tmphstbuf = (char *)realloc(*tmphstbuf, *hstbuflen); + *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, true); } } memset((void *)(*tmphstbuf),0,*hstbuflen); diff --git a/main/php_ini.c b/main/php_ini.c index 9925eafad1fd..43e1c115e6ea 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -694,7 +694,7 @@ void php_init_config(void) if (total_l) { size_t php_ini_scanned_files_len = (php_ini_scanned_files) ? strlen(php_ini_scanned_files) + 1 : 0; - php_ini_scanned_files = (char *) realloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1); + php_ini_scanned_files = (char *) perealloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1, true); if (!php_ini_scanned_files_len) { *php_ini_scanned_files = '\0'; } diff --git a/main/php_ini_builder.h b/main/php_ini_builder.h index 0ad70b6f68a7..9f73cba13cb7 100644 --- a/main/php_ini_builder.h +++ b/main/php_ini_builder.h @@ -60,7 +60,7 @@ static inline char *php_ini_builder_finish(struct php_ini_builder *b) static inline void php_ini_builder_realloc(struct php_ini_builder *b, size_t delta) { /* reserve enough space for the null terminator */ - b->value = realloc(b->value, b->length + delta + 1); + b->value = perealloc(b->value, b->length + delta + 1, true); } /** diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index 278e2657862f..3c0d5e836dd0 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -1199,9 +1199,9 @@ int main(int argc, char **argv) /* {{{ */ case 'z': zend_extensions_len++; if (zend_extensions_list) { - zend_extensions_list = realloc(zend_extensions_list, sizeof(char*) * zend_extensions_len); + zend_extensions_list = perealloc(zend_extensions_list, sizeof(char*) * zend_extensions_len, true); } else { - zend_extensions_list = malloc(sizeof(char*) * zend_extensions_len); + zend_extensions_list = pemalloc(sizeof(char*) * zend_extensions_len, true); } zend_extensions_list[zend_extensions_len-1] = strdup(php_optarg); break; diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index 9435e25ca00b..692cda32c7ea 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -215,9 +215,9 @@ static void phpdbg_line_init(char *cmd, struct phpdbg_init_state *state) { if (state->in_code) { if (state->code == NULL) { - state->code = malloc(cmd_len + 1); + state->code = pemalloc(cmd_len + 1, true); } else { - state->code = realloc(state->code, state->code_len + cmd_len + 1); + state->code = perealloc(state->code, state->code_len + cmd_len + 1, true); } if (state->code) {