From f8bd3cf382fbf816c0e0db4aceb9bdd7dbe4068c Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 25 Jan 2025 19:43:21 +0000 Subject: [PATCH] ext/session: start to implement GH-14019 --- ext/session/config.m4 | 46 +++++---- ext/session/mod_mm.c | 222 +++++++++++++----------------------------- ext/session/mod_mm.h | 2 +- 3 files changed, 89 insertions(+), 181 deletions(-) diff --git a/ext/session/config.m4 b/ext/session/config.m4 index 36cfdf10972ab..d3ceffde30dc4 100644 --- a/ext/session/config.m4 +++ b/ext/session/config.m4 @@ -14,10 +14,7 @@ PHP_ARG_WITH([mm], if test "$PHP_SESSION" != "no"; then PHP_PWRITE_TEST PHP_PREAD_TEST - PHP_NEW_EXTENSION([session], - [mod_user_class.c session.c mod_files.c mod_mm.c mod_user.c], - [$ext_shared],, - [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) + SESSION_CFLAGS="-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1" PHP_ADD_EXTENSION_DEP(session, date) dnl https://bugs.php.net/53141 @@ -29,26 +26,27 @@ if test "$PHP_SESSION" != "no"; then [Define to 1 if the PHP extension 'session' is available.]) AS_VAR_IF([PHP_MM], [no],, [ - for i in $PHP_MM /usr/local /usr; do - AS_IF([test -f "$i/include/mm.h"], [MM_DIR=$i; break;]) - done - - AS_VAR_IF([MM_DIR],, - [AC_MSG_ERROR([Cannot find the 'mm' library, header file not found.])]) - - AS_VAR_IF([PHP_THREAD_SAFETY], [yes], [AC_MSG_ERROR(m4_text_wrap([ - The configure option '--with-mm' cannot be combined with '--enable-zts'. - The mm library is not thread-safe, and mod_mm.c refuses to compile. Either - remove the '--with-mm' option, or build without thread safety (remove the - '--enable-zts' option). - ]))]) - - PHP_ADD_LIBRARY_WITH_PATH([mm], - [$MM_DIR/$PHP_LIBDIR], - [SESSION_SHARED_LIBADD]) - PHP_ADD_INCLUDE([$MM_DIR/include]) + PKG_CHECK_MODULES([LIBGLIB], [glib-2.0 >= 2.0]) + + PHP_EVAL_INCLINE($[LIBGLIB_CFLAGS]) + PHP_EVAL_LIBLINE($[LIBGLIB_LIBS], [GLIB_SHARED_LIBADD]) + PHP_CHECK_LIBRARY([glib-2.0], [g_try_malloc], + [AC_DEFINE([HAVE_GLIB2], [1], + [Define to 1 if libglib-2.0 library has 'g_try_malloc' function.])], + [] + [$GLIB_LIBS]) + + SESSION_CFLAGS="$SESSION_CFLAGS $LIBGLIB_CFLAGS" + PHP_SUBST([GLIB_SHARED_LIBADD]) + + PHP_INSTALL_HEADERS([ext/session], [mod_mm.h]) - AC_DEFINE([HAVE_LIBMM], [1], - [Define to 1 if the system has the 'mm' library.]) + AC_DEFINE([HAVE_LIBGLIB], [1], + [Define to 1 if the system has the 'glib' library.]) ]) + + PHP_NEW_EXTENSION([session], + [mod_user_class.c session.c mod_files.c mod_mm.c mod_user.c], + [$ext_shared],, + [$SESSION_CFLAGS]) fi diff --git a/ext/session/mod_mm.c b/ext/session/mod_mm.c index b997a2bdcff54..a676a59c1829b 100644 --- a/ext/session/mod_mm.c +++ b/ext/session/mod_mm.c @@ -16,10 +16,10 @@ #include "php.h" -#ifdef HAVE_LIBMM +#ifdef HAVE_LIBGLIB #include -#include +#include #include #include #include @@ -30,32 +30,31 @@ #include "mod_mm.h" #include "SAPI.h" -#ifdef ZTS -# error mm is not thread-safe -#endif - #define PS_MM_FILE "session_mm_" /* This list holds all data associated with one session. */ typedef struct ps_sd { - struct ps_sd *next; - uint32_t hv; /* hash value of key */ time_t ctime; /* time of last change */ void *data; size_t datalen; /* amount of valid data */ size_t alloclen; /* amount of allocated memory for data */ - zend_string *key; } ps_sd; typedef struct { - MM *mm; - ps_sd **hash; + GMappedFile *mm; + GHashTable *hash; uint32_t hash_max; uint32_t hash_cnt; pid_t owner; } ps_mm; +typedef struct { + ps_mm *data; + zend_long maxlifetime; + zend_long *nrdels; +} ps_timelimit; + static ps_mm *ps_mm_instance = NULL; #if 0 @@ -78,135 +77,51 @@ static inline uint32_t ps_sd_hash(const zend_string *data) return h; } -static void hash_split(ps_mm *data) -{ - uint32_t nmax; - ps_sd **nhash; - ps_sd **ohash, **ehash; - ps_sd *ps, *next; - - nmax = ((data->hash_max + 1) << 1) - 1; - nhash = mm_calloc(data->mm, nmax + 1, sizeof(*data->hash)); - - if (!nhash) { - /* no further memory to expand hash table */ - return; - } - - ehash = data->hash + data->hash_max + 1; - for (ohash = data->hash; ohash < ehash; ohash++) { - for (ps = *ohash; ps; ps = next) { - next = ps->next; - ps->next = nhash[ps->hv & nmax]; - nhash[ps->hv & nmax] = ps; - } - } - mm_free(data->mm, data->hash); - - data->hash = nhash; - data->hash_max = nmax; -} - static ps_sd *ps_sd_new(ps_mm *data, zend_string *key) { - uint32_t hv, slot; ps_sd *sd; - sd = mm_malloc(data->mm, sizeof(ps_sd) + ZSTR_LEN(key)); + sd = g_malloc(sizeof(ps_sd) + ZSTR_LEN(key)); if (!sd) { - php_error_docref(NULL, E_WARNING, "mm_malloc failed, avail %ld, err %s", mm_available(data->mm), mm_error()); + //php_error_docref(NULL, E_WARNING, "g_malloc failed, avail %ld, err %s", mm_available(data->mm), mm_error()); return NULL; } - hv = ps_sd_hash(key); - slot = hv & data->hash_max; - sd->ctime = 0; - sd->hv = hv; sd->data = NULL; sd->alloclen = sd->datalen = 0; - sd->key = zend_string_copy(key); - - sd->next = data->hash[slot]; - data->hash[slot] = sd; + g_hash_table_insert(data->hash, key, sd); data->hash_cnt++; - if (!sd->next) { - if (data->hash_cnt >= data->hash_max) { - hash_split(data); - } - } - - ps_mm_debug(("inserting %s(%p) into slot %d\n", ZSTR_VAL(key), sd, slot)); - return sd; } -static void ps_sd_destroy(ps_mm *data, ps_sd *sd) +static void ps_sd_destroy(ps_mm *data, zend_string *key, ps_sd *sd) { - uint32_t slot; - - slot = ps_sd_hash(sd->key) & data->hash_max; - - if (data->hash[slot] == sd) { - data->hash[slot] = sd->next; - } else { - ps_sd *prev; - - /* There must be some entry before the one we want to delete */ - for (prev = data->hash[slot]; prev->next != sd; prev = prev->next); - prev->next = sd->next; - } - data->hash_cnt--; if (sd->data) { - mm_free(data->mm, sd->data); + g_free(sd->data); } - zend_string_release(sd->key); - mm_free(data->mm, sd); + g_hash_table_remove(data->hash, key); } -static ps_sd *ps_sd_lookup(ps_mm *data, const zend_string *key, bool rw) +static ps_sd *ps_sd_lookup(ps_mm *data, const zend_string *key, bool _rw) { - uint32_t hv, slot; - ps_sd *ret, *prev; - - hv = ps_sd_hash(key); - slot = hv & data->hash_max; - - for (prev = NULL, ret = data->hash[slot]; ret; prev = ret, ret = ret->next) { - if (ret->hv == hv && zend_string_equals(ret->key, key)) { - break; - } - } - - if (ret && rw && ret != data->hash[slot]) { - /* Move the entry to the top of the linked list */ - if (prev) { - prev->next = ret->next; - } - - ret->next = data->hash[slot]; - data->hash[slot] = ret; - } - - ps_mm_debug(("lookup(%s): ret=%p,hv=%u,slot=%d\n", ZSTR_VAL(key), ret, hv, slot)); - - return ret; + return (ps_sd *)g_hash_table_lookup(data->hash, key); } static zend_result ps_mm_key_exists(ps_mm *data, const zend_string *key) { ps_sd *sd; - if (!key) { return FAILURE; } + sd = ps_sd_lookup(data, key, 0); if (sd) { return SUCCESS; @@ -220,29 +135,58 @@ const ps_module ps_mod_mm = { #define PS_MM_DATA ps_mm *data = PS_GET_MOD_DATA() +static guint ps_mm_hash(gconstpointer key) { + return (guint)(zend_string_hash_val((zend_string *)key)); +} + +static gboolean ps_mm_key_equals(gconstpointer a, gconstpointer b) { + return zend_string_equals((const zend_string *)a, (const zend_string *)b); +} + static zend_result ps_mm_initialize(ps_mm *data, const char *path) { data->owner = getpid(); - data->mm = mm_create(0, path); + data->mm = g_mapped_file_new(path, TRUE, NULL); if (!data->mm) { return FAILURE; } data->hash_cnt = 0; data->hash_max = 511; - data->hash = mm_calloc(data->mm, data->hash_max + 1, sizeof(ps_sd *)); + data->hash = g_hash_table_new(ps_mm_hash, ps_mm_key_equals); if (!data->hash) { - mm_destroy(data->mm); + g_mapped_file_unref(data->mm); return FAILURE; } return SUCCESS; } -static void ps_mm_destroy(ps_mm *data) +static gboolean ps_mm_destroy_entry(gpointer key, gpointer val, gpointer _priv) { - ps_sd *sd, *next; + zend_string_release_ex((zend_string *)key, false); + ps_sd *sd = (ps_sd *)val; + g_free(sd); + return TRUE; +} + +static void ps_mm_timelimit(gpointer key, gpointer val, gpointer _priv) +{ + time_t limit; + time(&limit); + ps_timelimit *ps_tm = (ps_timelimit *)_priv; + + limit -= ps_tm->maxlifetime; + + ps_sd *sd = (ps_sd *)val; + if (sd->ctime < limit) { + ps_sd_destroy(ps_tm->data, (zend_string *)key, sd); + (*ps_tm->nrdels)++; + } +} +static void ps_mm_destroy(ps_mm *data) +{ /* This function is called during each module shutdown, but we must not release the shared memory pool, when an Apache child dies! */ @@ -250,15 +194,10 @@ static void ps_mm_destroy(ps_mm *data) return; } - for (int h = 0; h < data->hash_max + 1; h++) { - for (sd = data->hash[h]; sd; sd = next) { - next = sd->next; - ps_sd_destroy(data, sd); - } - } + g_hash_table_foreach_remove(data->hash, ps_mm_destroy_entry, NULL); - mm_free(data->mm, data->hash); - mm_destroy(data->mm); + g_hash_table_destroy(data->hash); + g_mapped_file_unref(data->mm); free(data); } @@ -270,7 +209,7 @@ PHP_MINIT_FUNCTION(ps_mm) char *ps_mm_path, euid[30]; zend_result ret; - ps_mm_instance = calloc(sizeof(*ps_mm_instance), 1); + ps_mm_instance = calloc(1, sizeof(*ps_mm_instance)); if (!ps_mm_instance) { return FAILURE; } @@ -344,8 +283,6 @@ PS_READ_FUNC(mm) ps_sd *sd; zend_result ret = FAILURE; - mm_lock(data->mm, MM_LOCK_RD); - /* If there is an ID and strict mode, verify existence */ if (PS(use_strict_mode) && ps_mm_key_exists(data, key) == FAILURE) { @@ -367,12 +304,10 @@ PS_READ_FUNC(mm) sd = ps_sd_lookup(data, PS(id), 0); if (sd) { - *val = zend_string_init(sd->data, sd->datalen, 0); + *val = zend_string_init(sd->data, sd->datalen, false); ret = SUCCESS; } - mm_unlock(data->mm); - return ret; } @@ -381,8 +316,6 @@ PS_WRITE_FUNC(mm) PS_MM_DATA; ps_sd *sd; - mm_lock(data->mm, MM_LOCK_RW); - sd = ps_sd_lookup(data, key, 1); if (!sd) { sd = ps_sd_new(data, key); @@ -391,14 +324,11 @@ PS_WRITE_FUNC(mm) if (sd) { if (val->len >= sd->alloclen) { - if (data->mm) { - mm_free(data->mm, sd->data); - } sd->alloclen = val->len + 1; - sd->data = mm_malloc(data->mm, sd->alloclen); + sd->data = g_realloc(sd->data, sd->alloclen); if (!sd->data) { - ps_sd_destroy(data, sd); + ps_sd_destroy(data, key, sd); php_error_docref(NULL, E_WARNING, "Cannot allocate new data segment"); sd = NULL; } @@ -410,8 +340,6 @@ PS_WRITE_FUNC(mm) } } - mm_unlock(data->mm); - return sd ? SUCCESS : FAILURE; } @@ -420,47 +348,29 @@ PS_DESTROY_FUNC(mm) PS_MM_DATA; ps_sd *sd; - mm_lock(data->mm, MM_LOCK_RW); - sd = ps_sd_lookup(data, key, 0); if (sd) { - ps_sd_destroy(data, sd); + ps_sd_destroy(data, key, sd); } - mm_unlock(data->mm); - return SUCCESS; } PS_GC_FUNC(mm) { PS_MM_DATA; - time_t limit; - ps_sd **ohash, **ehash; - ps_sd *sd, *next; *nrdels = 0; ps_mm_debug(("gc\n")); - time(&limit); - - limit -= maxlifetime; + ps_timelimit ps_tm; - mm_lock(data->mm, MM_LOCK_RW); + ps_tm.data = data; + ps_tm.maxlifetime = maxlifetime; + ps_tm.nrdels = nrdels; - ehash = data->hash + data->hash_max + 1; - for (ohash = data->hash; ohash < ehash; ohash++) { - for (sd = *ohash; sd; sd = next) { - next = sd->next; - if (sd->ctime < limit) { - ps_mm_debug(("purging %s\n", ZSTR_VAL(sd->key))); - ps_sd_destroy(data, sd); - (*nrdels)++; - } - } - } - mm_unlock(data->mm); + g_hash_table_foreach(data->hash, ps_mm_timelimit, &ps_tm); return *nrdels; } diff --git a/ext/session/mod_mm.h b/ext/session/mod_mm.h index b35e0b632ee91..758a5cc38e625 100644 --- a/ext/session/mod_mm.h +++ b/ext/session/mod_mm.h @@ -17,7 +17,7 @@ #ifndef MOD_MM_H #define MOD_MM_H -#ifdef HAVE_LIBMM +#ifdef HAVE_LIBGLIB #include "php_session.h"