123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- /*
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | https://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Author: Sascha Schumann <sascha@schumann.cx> |
- +----------------------------------------------------------------------+
- */
- #include "php.h"
- #ifdef HAVE_LIBMM
- #include <unistd.h>
- #include <mm.h>
- #include <time.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include "php_stdint.h"
- #include "php_session.h"
- #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 */
- char key[1]; /* inline key */
- } ps_sd;
- typedef struct {
- MM *mm;
- ps_sd **hash;
- uint32_t hash_max;
- uint32_t hash_cnt;
- pid_t owner;
- } ps_mm;
- static ps_mm *ps_mm_instance = NULL;
- #if 0
- # define ps_mm_debug(a) printf a
- #else
- # define ps_mm_debug(a)
- #endif
- static inline uint32_t ps_sd_hash(const char *data, int len)
- {
- uint32_t h;
- const char *e = data + len;
- for (h = 2166136261U; data < e; ) {
- h *= 16777619;
- h ^= *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, const char *key)
- {
- uint32_t hv, slot;
- ps_sd *sd;
- int keylen;
- keylen = strlen(key);
- sd = mm_malloc(data->mm, sizeof(ps_sd) + keylen);
- if (!sd) {
- php_error_docref(NULL, E_WARNING, "mm_malloc failed, avail %ld, err %s", mm_available(data->mm), mm_error());
- return NULL;
- }
- hv = ps_sd_hash(key, keylen);
- slot = hv & data->hash_max;
- sd->ctime = 0;
- sd->hv = hv;
- sd->data = NULL;
- sd->alloclen = sd->datalen = 0;
- memcpy(sd->key, key, keylen + 1);
- sd->next = data->hash[slot];
- data->hash[slot] = 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", key, sd, slot));
- return sd;
- }
- static void ps_sd_destroy(ps_mm *data, ps_sd *sd)
- {
- uint32_t slot;
- slot = ps_sd_hash(sd->key, strlen(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);
- }
- mm_free(data->mm, sd);
- }
- static ps_sd *ps_sd_lookup(ps_mm *data, const char *key, int rw)
- {
- uint32_t hv, slot;
- ps_sd *ret, *prev;
- hv = ps_sd_hash(key, strlen(key));
- slot = hv & data->hash_max;
- for (prev = NULL, ret = data->hash[slot]; ret; prev = ret, ret = ret->next) {
- if (ret->hv == hv && !strcmp(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", key, ret, hv, slot));
- return ret;
- }
- static int ps_mm_key_exists(ps_mm *data, const char *key)
- {
- ps_sd *sd;
- if (!key) {
- return FAILURE;
- }
- sd = ps_sd_lookup(data, key, 0);
- if (sd) {
- return SUCCESS;
- }
- return FAILURE;
- }
- const ps_module ps_mod_mm = {
- PS_MOD_SID(mm)
- };
- #define PS_MM_DATA ps_mm *data = PS_GET_MOD_DATA()
- static int ps_mm_initialize(ps_mm *data, const char *path)
- {
- data->owner = getpid();
- data->mm = mm_create(0, path);
- 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 *));
- if (!data->hash) {
- mm_destroy(data->mm);
- return FAILURE;
- }
- return SUCCESS;
- }
- static void ps_mm_destroy(ps_mm *data)
- {
- int h;
- ps_sd *sd, *next;
- /* This function is called during each module shutdown,
- but we must not release the shared memory pool, when
- an Apache child dies! */
- if (data->owner != getpid()) {
- return;
- }
- for (h = 0; h < data->hash_max + 1; h++) {
- for (sd = data->hash[h]; sd; sd = next) {
- next = sd->next;
- ps_sd_destroy(data, sd);
- }
- }
- mm_free(data->mm, data->hash);
- mm_destroy(data->mm);
- free(data);
- }
- PHP_MINIT_FUNCTION(ps_mm)
- {
- int save_path_len = strlen(PS(save_path));
- int mod_name_len = strlen(sapi_module.name);
- int euid_len;
- char *ps_mm_path, euid[30];
- int ret;
- ps_mm_instance = calloc(sizeof(*ps_mm_instance), 1);
- if (!ps_mm_instance) {
- return FAILURE;
- }
- if (!(euid_len = slprintf(euid, sizeof(euid), "%d", geteuid()))) {
- free(ps_mm_instance);
- ps_mm_instance = NULL;
- return FAILURE;
- }
- /* Directory + '/' + File + Module Name + Effective UID + \0 */
- ps_mm_path = emalloc(save_path_len + 1 + (sizeof(PS_MM_FILE) - 1) + mod_name_len + euid_len + 1);
- memcpy(ps_mm_path, PS(save_path), save_path_len);
- if (save_path_len && PS(save_path)[save_path_len - 1] != DEFAULT_SLASH) {
- ps_mm_path[save_path_len] = DEFAULT_SLASH;
- save_path_len++;
- }
- memcpy(ps_mm_path + save_path_len, PS_MM_FILE, sizeof(PS_MM_FILE) - 1);
- save_path_len += sizeof(PS_MM_FILE) - 1;
- memcpy(ps_mm_path + save_path_len, sapi_module.name, mod_name_len);
- save_path_len += mod_name_len;
- memcpy(ps_mm_path + save_path_len, euid, euid_len);
- ps_mm_path[save_path_len + euid_len] = '\0';
- ret = ps_mm_initialize(ps_mm_instance, ps_mm_path);
- efree(ps_mm_path);
- if (ret != SUCCESS) {
- free(ps_mm_instance);
- ps_mm_instance = NULL;
- return FAILURE;
- }
- php_session_register_module(&ps_mod_mm);
- return SUCCESS;
- }
- PHP_MSHUTDOWN_FUNCTION(ps_mm)
- {
- if (ps_mm_instance) {
- ps_mm_destroy(ps_mm_instance);
- return SUCCESS;
- }
- return FAILURE;
- }
- PS_OPEN_FUNC(mm)
- {
- ps_mm_debug(("open: ps_mm_instance=%p\n", ps_mm_instance));
- if (!ps_mm_instance) {
- return FAILURE;
- }
- PS_SET_MOD_DATA(ps_mm_instance);
- return SUCCESS;
- }
- PS_CLOSE_FUNC(mm)
- {
- PS_SET_MOD_DATA(NULL);
- return SUCCESS;
- }
- PS_READ_FUNC(mm)
- {
- PS_MM_DATA;
- ps_sd *sd;
- int 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->val) == FAILURE) {
- /* key points to PS(id), but cannot change here. */
- if (key) {
- efree(PS(id));
- PS(id) = NULL;
- }
- PS(id) = PS(mod)->s_create_sid((void **)&data);
- if (!PS(id)) {
- return FAILURE;
- }
- if (PS(use_cookies)) {
- PS(send_cookie) = 1;
- }
- php_session_reset_id();
- PS(session_status) = php_session_active;
- }
- sd = ps_sd_lookup(data, PS(id)->val, 0);
- if (sd) {
- *val = zend_string_init(sd->data, sd->datalen, 0);
- ret = SUCCESS;
- }
- mm_unlock(data->mm);
- return ret;
- }
- PS_WRITE_FUNC(mm)
- {
- PS_MM_DATA;
- ps_sd *sd;
- mm_lock(data->mm, MM_LOCK_RW);
- sd = ps_sd_lookup(data, key->val, 1);
- if (!sd) {
- sd = ps_sd_new(data, key->val);
- ps_mm_debug(("new entry for %s\n", key->val));
- }
- 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);
- if (!sd->data) {
- ps_sd_destroy(data, sd);
- php_error_docref(NULL, E_WARNING, "Cannot allocate new data segment");
- sd = NULL;
- }
- }
- if (sd) {
- sd->datalen = val->len;
- memcpy(sd->data, val->val, val->len);
- time(&sd->ctime);
- }
- }
- mm_unlock(data->mm);
- return sd ? SUCCESS : FAILURE;
- }
- PS_DESTROY_FUNC(mm)
- {
- PS_MM_DATA;
- ps_sd *sd;
- mm_lock(data->mm, MM_LOCK_RW);
- sd = ps_sd_lookup(data, key->val, 0);
- if (sd) {
- ps_sd_destroy(data, 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;
- mm_lock(data->mm, MM_LOCK_RW);
- 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", sd->key));
- ps_sd_destroy(data, sd);
- (*nrdels)++;
- }
- }
- }
- mm_unlock(data->mm);
- return *nrdels;
- }
- PS_CREATE_SID_FUNC(mm)
- {
- zend_string *sid;
- int maxfail = 3;
- PS_MM_DATA;
- do {
- sid = php_session_create_id((void **)&data);
- /* Check collision */
- if (ps_mm_key_exists(data, sid->val) == SUCCESS) {
- if (sid) {
- zend_string_release_ex(sid, 0);
- sid = NULL;
- }
- if (!(maxfail--)) {
- return NULL;
- }
- }
- } while(!sid);
- return sid;
- }
- #endif
|