From bfc2ba0659c23201fe138af3e93aa1355c4cb0f3 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=1 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 | 22 +++++++++++----------- 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, 27 insertions(+), 35 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index b4a084b1f95c7..bb689790ab2b7 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1296,7 +1296,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, 1); 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 e529c48b5ac2a..bd2e5c4439ea3 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2522,19 +2522,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), 1); 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), 1); modules_dl_loaded[dl_loaded_count] = NULL; startup_count = 0; @@ -2561,10 +2561,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), 1); class_cleanup_handlers[class_count] = NULL; if (class_count) { @@ -3076,7 +3076,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), 1); memcpy(reg_function, &function, sizeof(zend_internal_function)); if (zend_hash_add_ptr(target_function_table, lowercase_name, reg_function) == NULL) { unload=1; @@ -3094,8 +3094,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 *), 1); + zend_flf_functions = perealloc(zend_flf_functions, (zend_flf_capacity + 1) * sizeof(zend_function *), 1); } zend_flf_handlers[zend_flf_count] = flf_info->handler; zend_flf_functions[zend_flf_count] = (zend_function *)reg_function; @@ -3143,7 +3143,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_internal_arg_info) * num_args); + new_arg_info = pemalloc(sizeof(zend_internal_arg_info) * num_args, 1); memcpy(new_arg_info, arg_info, sizeof(zend_internal_arg_info) * num_args); reg_function->arg_info = new_arg_info + 1; for (i = 0; i < num_args; i++) { @@ -3169,7 +3169,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend new_arg_info[i].type.type_mask |= _ZEND_TYPE_NAME_BIT; } else { /* Union type */ - zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types)); + zend_type_list *list = pemalloc(ZEND_TYPE_LIST_SIZE(num_types), 1); list->num_types = num_types; ZEND_TYPE_SET_LIST(new_arg_info[i].type, list); ZEND_TYPE_FULL_MASK(new_arg_info[i].type) |= _ZEND_TYPE_UNION_BIT; @@ -3480,7 +3480,7 @@ ZEND_API int zend_next_free_module(void) /* {{{ */ static zend_class_entry *do_register_internal_class(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), 1); zend_string *lowercase_name; *class_entry = *orig_class_entry; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 56e5bdb9295a6..99f8fc5309e66 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1573,11 +1573,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--) { @@ -2206,11 +2202,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 c4a543e6207bb..67a602061b467 100644 --- a/ext/opcache/zend_accelerator_blacklist.c +++ b/ext/opcache/zend_accelerator_blacklist.c @@ -233,7 +233,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, 1); } } diff --git a/main/network.c b/main/network.c index 40a80a6f2abc0..6b37e7024cd01 100644 --- a/main/network.c +++ b/main/network.c @@ -1345,7 +1345,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, 1); } while (( res = @@ -1353,7 +1353,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, 1); } if (res != 0) { @@ -1371,7 +1371,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, 1); } while ((NULL == ( hp = @@ -1379,7 +1379,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, 1); } return hp; } @@ -1389,11 +1389,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, 1); } else { if (*hstbuflen < sizeof(struct hostent_data)) { *hstbuflen = sizeof(struct hostent_data); - *tmphstbuf = (char *)realloc(*tmphstbuf, *hstbuflen); + *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, 1); } } memset((void *)(*tmphstbuf),0,*hstbuflen); diff --git a/main/php_ini.c b/main/php_ini.c index 2fc4cca26553f..6659fdc4c68ce 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -699,7 +699,7 @@ int php_init_config(void) if (total_l) { int php_ini_scanned_files_len = (php_ini_scanned_files) ? (int)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, 1); 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 7f5be81c10ac7..b2be46890b875 100644 --- a/main/php_ini_builder.h +++ b/main/php_ini_builder.h @@ -62,7 +62,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, 1); } /** diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index 58efd8e554c76..12198f90f0fd9 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -1207,8 +1207,8 @@ int main(int argc, char **argv) /* {{{ */ case 'z': zend_extensions_len++; if (zend_extensions) { - zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len); - } else zend_extensions = malloc(sizeof(char*) * zend_extensions_len); + zend_extensions = perealloc(zend_extensions, sizeof(char*) * zend_extensions_len, 1); + } else zend_extensions = pemalloc(sizeof(char*) * zend_extensions_len, 1); zend_extensions[zend_extensions_len-1] = strdup(php_optarg); break; diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index 469263b517da8..89a43e9f4311b 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -240,9 +240,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, 1); } else { - state->code = realloc(state->code, state->code_len + cmd_len + 1); + state->code = perealloc(state->code, state->code_len + cmd_len + 1, 1); } if (state->code) {