From 5c326244d9eef8ed81608ef859f033e83e9d2725 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 26 Oct 2024 11:25:57 +0200 Subject: [PATCH 1/2] Use ZendMM in ext-gmp While libgmp uses true globals to track the allocator, and is therefore not safe to use in a threaded environment, we can make use of a thread-local variable that indicates whether the current thread is currently performing a PHP request. We set this TLS global to true if it is in RINIT, and set it to false in post-RSHUTDOWN. This ensures that any (de)allocation during the request will go through ZendMM while keeping it possible for this thread to be reused or for other threads using libgmp. Co-authored-by: "Christoph M. Becker" --- ext/gmp/gmp.c | 57 +++++++++++++++++++++++++++++++++++++++++++++-- ext/gmp/php_gmp.h | 2 ++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 97172a13a962c..81a53986f8b9d 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -59,14 +59,14 @@ zend_module_entry gmp_module_entry = { ext_functions, ZEND_MODULE_STARTUP_N(gmp), NULL, - NULL, + ZEND_MODULE_ACTIVATE_N(gmp), ZEND_MODULE_DEACTIVATE_N(gmp), ZEND_MODULE_INFO_N(gmp), PHP_GMP_VERSION, ZEND_MODULE_GLOBALS(gmp), ZEND_GINIT(gmp), NULL, - NULL, + ZEND_MODULE_POST_ZEND_DEACTIVATE_N(gmp), STANDARD_MODULE_PROPERTIES_EX }; /* }}} */ @@ -81,6 +81,16 @@ ZEND_GET_MODULE(gmp) static zend_class_entry *gmp_ce; static zend_object_handlers gmp_object_handlers; +/* Allocator state: `gmp_request_allocator` is a true TLS global + * to indicate whether this thread is currently working on a PHP request. + * Just checking EG() is not enough because other modules may be loaded in the webserver. */ +static TSRM_TLS bool gmp_request_allocator = false; +/* True globals for the old allocator, as these are global state in gmplib, + * this doesn't need to be TLS. */ +static void *(*gmp_old_alloc)(size_t); +static void *(*gmp_old_realloc)(void *, size_t, size_t); +static void (*gmp_old_free)(void *, size_t); + PHP_GMP_API zend_class_entry *php_gmp_class_entry(void) { return gmp_ce; } @@ -521,6 +531,33 @@ static int gmp_unserialize(zval *object, zend_class_entry *ce, const unsigned ch } /* }}} */ +static void *gmp_alloc(size_t size) +{ + if (gmp_request_allocator) { + return emalloc(size); + } else { + return gmp_old_alloc(size); + } +} + +static void *gmp_realloc(void *ptr, size_t old_size, size_t new_size) +{ + if (gmp_request_allocator) { + return erealloc(ptr, new_size); + } else { + return gmp_old_realloc(ptr, old_size, new_size); + } +} + +static void gmp_free(void *ptr, size_t size) +{ + if (gmp_request_allocator) { + efree_size(ptr, size); + } else { + return gmp_old_free(ptr, size); + } +} + /* {{{ ZEND_GINIT_FUNCTION */ static ZEND_GINIT_FUNCTION(gmp) { @@ -551,10 +588,19 @@ ZEND_MINIT_FUNCTION(gmp) register_gmp_symbols(module_number); + mp_get_memory_functions(&gmp_old_alloc, &gmp_old_realloc, &gmp_old_free); + mp_set_memory_functions(gmp_alloc, gmp_realloc, gmp_free); + return SUCCESS; } /* }}} */ +ZEND_MODULE_ACTIVATE_D(gmp) +{ + gmp_request_allocator = true; + return SUCCESS; +} + /* {{{ ZEND_RSHUTDOWN_FUNCTION */ ZEND_MODULE_DEACTIVATE_D(gmp) { @@ -567,6 +613,13 @@ ZEND_MODULE_DEACTIVATE_D(gmp) } /* }}} */ +ZEND_MODULE_POST_ZEND_DEACTIVATE_D(gmp) +{ + /* We have to deactivate it after all request state has been freed. */ + gmp_request_allocator = false; + return SUCCESS; +} + /* {{{ ZEND_MINFO_FUNCTION */ ZEND_MODULE_INFO_D(gmp) { diff --git a/ext/gmp/php_gmp.h b/ext/gmp/php_gmp.h index 1c03a6ee8ceb2..2bffc87518168 100644 --- a/ext/gmp/php_gmp.h +++ b/ext/gmp/php_gmp.h @@ -26,7 +26,9 @@ extern zend_module_entry gmp_module_entry; #define PHP_GMP_VERSION PHP_VERSION ZEND_MODULE_STARTUP_D(gmp); +ZEND_MODULE_ACTIVATE_D(gmp); ZEND_MODULE_DEACTIVATE_D(gmp); +ZEND_MODULE_POST_ZEND_DEACTIVATE_D(gmp); ZEND_MODULE_INFO_D(gmp); ZEND_BEGIN_MODULE_GLOBALS(gmp) From d816fc3f0a625e5c01219d92add034bfca034278 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 26 Oct 2024 11:30:15 +0200 Subject: [PATCH 2/2] no return --- ext/gmp/gmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 81a53986f8b9d..80e78bc0e9232 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -554,7 +554,7 @@ static void gmp_free(void *ptr, size_t size) if (gmp_request_allocator) { efree_size(ptr, size); } else { - return gmp_old_free(ptr, size); + gmp_old_free(ptr, size); } }