diff --git a/src/cpp/main.cc b/src/cpp/main.cc index 2db6d092..65b4708d 100644 --- a/src/cpp/main.cc +++ b/src/cpp/main.cc @@ -20,13 +20,15 @@ int main(int cpp_argc, char **cpp_argv) { zend_throw_exception_hook = throw_exception; zend_module_entry *module = php_embed_get_module(); - if (zend_register_module_ex(module, MODULE_PERSISTENT) == NULL) { + if (zend_register_module_ex(module, MODULE_PERSISTENT) == NULL) { zend_error(E_ERROR, "Failed to register module [%s]", module->name); - } + return 255; + } - if (zend_startup_module_ex(module) == FAILURE) { + if (zend_startup_module_ex(module) == FAILURE) { zend_error(E_ERROR, "Failed to startup module [%s]", module->name); - } + return 255; + } int rc = 0; #if PPROF_ON @@ -50,10 +52,15 @@ int main(int cpp_argc, char **cpp_argv) { php_app_clean(); php::request_shutdown(); + /** + * There is a bug in PHP's handling of internal strings. All interned strings are released in the request shutdown + * function, but then released again in the php_embed_shutdown function, resulting in a use-after-free issue. These + * must be manually removed from the module table to prevent double release. + */ auto name_len = strlen(module->name); - auto lcname = zend_string_alloc(name_len, module->type == MODULE_PERSISTENT); - zend_str_tolower_copy(ZSTR_VAL(lcname), module->name, name_len); - zend_hash_del(&module_registry, lcname); + auto lcname = zend_string_alloc(name_len, module->type == MODULE_PERSISTENT); + zend_str_tolower_copy(ZSTR_VAL(lcname), module->name, name_len); + zend_hash_del(&module_registry, lcname); php_embed_shutdown();