php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
mod_mm.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Sascha Schumann <sascha@schumann.cx> |
14 +----------------------------------------------------------------------+
15 */
16
17#include "php.h"
18
19#ifdef HAVE_LIBMM
20
21#include <unistd.h>
22#include <mm.h>
23#include <time.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <fcntl.h>
27#include <stdint.h>
28
29#include "php_session.h"
30#include "mod_mm.h"
31#include "SAPI.h"
32
33#ifdef ZTS
34# error mm is not thread-safe
35#endif
36
37#define PS_MM_FILE "session_mm_"
38
39/* This list holds all data associated with one session. */
40
41typedef struct ps_sd {
42 struct ps_sd *next;
43 uint32_t hv; /* hash value of key */
44 time_t ctime; /* time of last change */
45 void *data;
46 size_t datalen; /* amount of valid data */
47 size_t alloclen; /* amount of allocated memory for data */
49} ps_sd;
50
51typedef struct {
52 MM *mm;
53 ps_sd **hash;
54 uint32_t hash_max;
55 uint32_t hash_cnt;
56 pid_t owner;
57} ps_mm;
58
59static ps_mm *ps_mm_instance = NULL;
60
61#if 0
62# define ps_mm_debug(a) printf a
63#else
64# define ps_mm_debug(a)
65#endif
66
67static inline uint32_t ps_sd_hash(const zend_string *data)
68{
69 uint32_t h;
70 const char *data_char = ZSTR_VAL(data);
71 const char *e = ZSTR_VAL(data) + ZSTR_LEN(data);
72
73 for (h = 2166136261U; data_char < e; ) {
74 h *= 16777619;
75 h ^= *data_char++;
76 }
77
78 return h;
79}
80
81static void hash_split(ps_mm *data)
82{
83 uint32_t nmax;
84 ps_sd **nhash;
85 ps_sd **ohash, **ehash;
86 ps_sd *ps, *next;
87
88 nmax = ((data->hash_max + 1) << 1) - 1;
89 nhash = mm_calloc(data->mm, nmax + 1, sizeof(*data->hash));
90
91 if (!nhash) {
92 /* no further memory to expand hash table */
93 return;
94 }
95
96 ehash = data->hash + data->hash_max + 1;
97 for (ohash = data->hash; ohash < ehash; ohash++) {
98 for (ps = *ohash; ps; ps = next) {
99 next = ps->next;
100 ps->next = nhash[ps->hv & nmax];
101 nhash[ps->hv & nmax] = ps;
102 }
103 }
104 mm_free(data->mm, data->hash);
105
106 data->hash = nhash;
107 data->hash_max = nmax;
108}
109
110static ps_sd *ps_sd_new(ps_mm *data, zend_string *key)
111{
112 uint32_t hv, slot;
113 ps_sd *sd;
114
115 sd = mm_malloc(data->mm, sizeof(ps_sd) + ZSTR_LEN(key));
116 if (!sd) {
117
118 php_error_docref(NULL, E_WARNING, "mm_malloc failed, avail %ld, err %s", mm_available(data->mm), mm_error());
119 return NULL;
120 }
121
122 hv = ps_sd_hash(key);
123 slot = hv & data->hash_max;
124
125 sd->ctime = 0;
126 sd->hv = hv;
127 sd->data = NULL;
128 sd->alloclen = sd->datalen = 0;
129
130 sd->key = zend_string_copy(key);
131
132 sd->next = data->hash[slot];
133 data->hash[slot] = sd;
134
135 data->hash_cnt++;
136
137 if (!sd->next) {
138 if (data->hash_cnt >= data->hash_max) {
139 hash_split(data);
140 }
141 }
142
143 ps_mm_debug(("inserting %s(%p) into slot %d\n", ZSTR_VAL(key), sd, slot));
144
145 return sd;
146}
147
148static void ps_sd_destroy(ps_mm *data, ps_sd *sd)
149{
150 uint32_t slot;
151
152 slot = ps_sd_hash(sd->key) & data->hash_max;
153
154 if (data->hash[slot] == sd) {
155 data->hash[slot] = sd->next;
156 } else {
157 ps_sd *prev;
158
159 /* There must be some entry before the one we want to delete */
160 for (prev = data->hash[slot]; prev->next != sd; prev = prev->next);
161 prev->next = sd->next;
162 }
163
164 data->hash_cnt--;
165
166 if (sd->data) {
167 mm_free(data->mm, sd->data);
168 }
169 zend_string_release(sd->key);
170
171 mm_free(data->mm, sd);
172}
173
174static ps_sd *ps_sd_lookup(ps_mm *data, const zend_string *key, bool rw)
175{
176 uint32_t hv, slot;
177 ps_sd *ret, *prev;
178
179 hv = ps_sd_hash(key);
180 slot = hv & data->hash_max;
181
182 for (prev = NULL, ret = data->hash[slot]; ret; prev = ret, ret = ret->next) {
183 if (ret->hv == hv && zend_string_equals(ret->key, key)) {
184 break;
185 }
186 }
187
188 if (ret && rw && ret != data->hash[slot]) {
189 /* Move the entry to the top of the linked list */
190 if (prev) {
191 prev->next = ret->next;
192 }
193
194 ret->next = data->hash[slot];
195 data->hash[slot] = ret;
196 }
197
198 ps_mm_debug(("lookup(%s): ret=%p,hv=%u,slot=%d\n", ZSTR_VAL(key), ret, hv, slot));
199
200 return ret;
201}
202
203static zend_result ps_mm_key_exists(ps_mm *data, const zend_string *key)
204{
205 ps_sd *sd;
206
207 if (!key) {
208 return FAILURE;
209 }
210 sd = ps_sd_lookup(data, key, 0);
211 if (sd) {
212 return SUCCESS;
213 }
214 return FAILURE;
215}
216
217const ps_module ps_mod_mm = {
218 PS_MOD_SID(mm)
219};
220
221#define PS_MM_DATA ps_mm *data = PS_GET_MOD_DATA()
222
223static zend_result ps_mm_initialize(ps_mm *data, const char *path)
224{
225 data->owner = getpid();
226 data->mm = mm_create(0, path);
227 if (!data->mm) {
228 return FAILURE;
229 }
230
231 data->hash_cnt = 0;
232 data->hash_max = 511;
233 data->hash = mm_calloc(data->mm, data->hash_max + 1, sizeof(ps_sd *));
234 if (!data->hash) {
235 mm_destroy(data->mm);
236 return FAILURE;
237 }
238
239 return SUCCESS;
240}
241
242static void ps_mm_destroy(ps_mm *data)
243{
244 ps_sd *sd, *next;
245
246 /* This function is called during each module shutdown,
247 but we must not release the shared memory pool, when
248 an Apache child dies! */
249 if (data->owner != getpid()) {
250 return;
251 }
252
253 for (int h = 0; h < data->hash_max + 1; h++) {
254 for (sd = data->hash[h]; sd; sd = next) {
255 next = sd->next;
256 ps_sd_destroy(data, sd);
257 }
258 }
259
260 mm_free(data->mm, data->hash);
261 mm_destroy(data->mm);
262 free(data);
263}
264
266{
267 size_t save_path_len = strlen(PS(save_path));
268 size_t mod_name_len = strlen(sapi_module.name);
269 size_t euid_len;
270 char *ps_mm_path, euid[30];
272
273 ps_mm_instance = calloc(sizeof(*ps_mm_instance), 1);
274 if (!ps_mm_instance) {
275 return FAILURE;
276 }
277
278 if (!(euid_len = slprintf(euid, sizeof(euid), "%d", geteuid()))) {
279 free(ps_mm_instance);
280 ps_mm_instance = NULL;
281 return FAILURE;
282 }
283
284 /* Directory + '/' + File + Module Name + Effective UID + \0 */
285 ps_mm_path = emalloc(save_path_len + 1 + (sizeof(PS_MM_FILE) - 1) + mod_name_len + euid_len + 1);
286
287 memcpy(ps_mm_path, PS(save_path), save_path_len);
288 if (save_path_len && PS(save_path)[save_path_len - 1] != DEFAULT_SLASH) {
289 ps_mm_path[save_path_len] = DEFAULT_SLASH;
290 save_path_len++;
291 }
292 memcpy(ps_mm_path + save_path_len, PS_MM_FILE, sizeof(PS_MM_FILE) - 1);
293 save_path_len += sizeof(PS_MM_FILE) - 1;
294 memcpy(ps_mm_path + save_path_len, sapi_module.name, mod_name_len);
295 save_path_len += mod_name_len;
296 memcpy(ps_mm_path + save_path_len, euid, euid_len);
297 ps_mm_path[save_path_len + euid_len] = '\0';
298
299 ret = ps_mm_initialize(ps_mm_instance, ps_mm_path);
300
301 efree(ps_mm_path);
302
303 if (ret == FAILURE) {
304 free(ps_mm_instance);
305 ps_mm_instance = NULL;
306 return FAILURE;
307 }
308
309 php_session_register_module(&ps_mod_mm);
310 return SUCCESS;
311}
312
314{
315 if (ps_mm_instance) {
316 ps_mm_destroy(ps_mm_instance);
317 return SUCCESS;
318 }
319 return FAILURE;
320}
321
322PS_OPEN_FUNC(mm)
323{
324 ps_mm_debug(("open: ps_mm_instance=%p\n", ps_mm_instance));
325
326 if (!ps_mm_instance) {
327 return FAILURE;
328 }
329 PS_SET_MOD_DATA(ps_mm_instance);
330
331 return SUCCESS;
332}
333
335{
337
338 return SUCCESS;
339}
340
341PS_READ_FUNC(mm)
342{
343 PS_MM_DATA;
344 ps_sd *sd;
346
347 mm_lock(data->mm, MM_LOCK_RD);
348
349 /* If there is an ID and strict mode, verify existence */
350 if (PS(use_strict_mode)
351 && ps_mm_key_exists(data, key) == FAILURE) {
352 /* key points to PS(id), but cannot change here. */
353 if (key) {
354 efree(PS(id));
355 PS(id) = NULL;
356 }
357 PS(id) = PS(mod)->s_create_sid((void **)&data);
358 if (!PS(id)) {
359 return FAILURE;
360 }
361 if (PS(use_cookies)) {
362 PS(send_cookie) = 1;
363 }
366 }
367
368 sd = ps_sd_lookup(data, PS(id), 0);
369 if (sd) {
370 *val = zend_string_init(sd->data, sd->datalen, 0);
371 ret = SUCCESS;
372 }
373
374 mm_unlock(data->mm);
375
376 return ret;
377}
378
380{
381 PS_MM_DATA;
382 ps_sd *sd;
383
384 mm_lock(data->mm, MM_LOCK_RW);
385
386 sd = ps_sd_lookup(data, key, 1);
387 if (!sd) {
388 sd = ps_sd_new(data, key);
389 ps_mm_debug(("new entry for %s\n", ZSTR_VAL(key)));
390 }
391
392 if (sd) {
393 if (val->len >= sd->alloclen) {
394 if (data->mm) {
395 mm_free(data->mm, sd->data);
396 }
397 sd->alloclen = val->len + 1;
398 sd->data = mm_malloc(data->mm, sd->alloclen);
399
400 if (!sd->data) {
401 ps_sd_destroy(data, sd);
402 php_error_docref(NULL, E_WARNING, "Cannot allocate new data segment");
403 sd = NULL;
404 }
405 }
406 if (sd) {
407 sd->datalen = val->len;
408 memcpy(sd->data, val->val, val->len);
409 time(&sd->ctime);
410 }
411 }
412
413 mm_unlock(data->mm);
414
415 return sd ? SUCCESS : FAILURE;
416}
417
419{
420 PS_MM_DATA;
421 ps_sd *sd;
422
423 mm_lock(data->mm, MM_LOCK_RW);
424
425 sd = ps_sd_lookup(data, key, 0);
426 if (sd) {
427 ps_sd_destroy(data, sd);
428 }
429
430 mm_unlock(data->mm);
431
432 return SUCCESS;
433}
434
435PS_GC_FUNC(mm)
436{
437 PS_MM_DATA;
438 time_t limit;
439 ps_sd **ohash, **ehash;
440 ps_sd *sd, *next;
441
442 *nrdels = 0;
443 ps_mm_debug(("gc\n"));
444
445 time(&limit);
446
447 limit -= maxlifetime;
448
449 mm_lock(data->mm, MM_LOCK_RW);
450
451 ehash = data->hash + data->hash_max + 1;
452 for (ohash = data->hash; ohash < ehash; ohash++) {
453 for (sd = *ohash; sd; sd = next) {
454 next = sd->next;
455 if (sd->ctime < limit) {
456 ps_mm_debug(("purging %s\n", ZSTR_VAL(sd->key)));
457 ps_sd_destroy(data, sd);
458 (*nrdels)++;
459 }
460 }
461 }
462
463 mm_unlock(data->mm);
464
465 return *nrdels;
466}
467
469{
470 zend_string *sid;
471 int maxfail = 3;
472 PS_MM_DATA;
473
474 do {
475 sid = php_session_create_id((void **)&data);
476 /* Check collision */
477 if (ps_mm_key_exists(data, sid) == SUCCESS) {
478 if (sid) {
480 sid = NULL;
481 }
482 if (!(maxfail--)) {
483 return NULL;
484 }
485 }
486 } while(!sid);
487
488 return sid;
489}
490
491#endif
SAPI_API sapi_module_struct sapi_module
Definition SAPI.c:65
prev(array|object &$array)
memcpy(ptr1, ptr2, size)
zval * val
Definition ffi.c:4262
#define NULL
Definition gdcache.h:45
hash(string $algo, string $data, bool $binary=false, array $options=[])
Definition hash.stub.php:12
#define SUCCESS
Definition hash_sha3.c:261
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
#define next(ls)
Definition minilua.c:2661
#define PHP_MSHUTDOWN_FUNCTION
Definition php.h:401
#define PHP_MINIT_FUNCTION
Definition php.h:400
time()
unsigned char key[REFLECTION_KEY_LEN]
@ php_session_active
PHPAPI zend_result php_session_register_module(const ps_module *)
Definition session.c:1181
struct ps_module_struct ps_module
PHPAPI zend_result php_session_reset_id(void)
Definition session.c:1538
#define PS_READ_FUNC(x)
Definition php_session.h:57
#define PS(v)
#define PS_SET_MOD_DATA(a)
Definition php_session.h:53
#define PS_DESTROY_FUNC(x)
Definition php_session.h:59
#define PS_GC_FUNC(x)
Definition php_session.h:60
#define PS_MOD_SID(x)
Definition php_session.h:92
#define PS_WRITE_FUNC(x)
Definition php_session.h:58
#define PS_CLOSE_FUNC(x)
Definition php_session.h:56
PHPAPI zend_string * php_session_create_id(PS_CREATE_SID_ARGS)
Definition session.c:335
#define PS_CREATE_SID_FUNC(x)
Definition php_session.h:61
#define PS_OPEN_FUNC(x)
Definition php_session.h:55
zend_constant * data
session_status()
#define slprintf
Definition snprintf.h:89
#define efree(ptr)
Definition zend_alloc.h:155
#define emalloc(size)
Definition zend_alloc.h:151
strlen(string $string)
zend_string_release_ex(func->internal_function.function_name, 0)
#define E_WARNING
Definition zend_errors.h:24
struct _zend_string zend_string
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
@ FAILURE
Definition zend_types.h:61
ZEND_RESULT_CODE zend_result
Definition zend_types.h:64
#define DEFAULT_SLASH
zval * ret