xref: /PHP-7.4/ext/standard/var_unserializer.re (revision 1806ce9c)
1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 7                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) The PHP Group                                          |
6  +----------------------------------------------------------------------+
7  | This source file is subject to version 3.01 of the PHP license,      |
8  | that is bundled with this package in the file LICENSE, and is        |
9  | available through the world-wide-web at the following url:           |
10  | http://www.php.net/license/3_01.txt                                  |
11  | If you did not receive a copy of the PHP license and are unable to   |
12  | obtain it through the world-wide-web, please send a note to          |
13  | license@php.net so we can mail you a copy immediately.               |
14  +----------------------------------------------------------------------+
15  | Author: Sascha Schumann <sascha@schumann.cx>                         |
16  +----------------------------------------------------------------------+
17*/
18
19#include "php.h"
20#include "ext/standard/php_var.h"
21#include "php_incomplete_class.h"
22#include "zend_portability.h"
23
24/* {{{ reference-handling for unserializer: var_* */
25#define VAR_ENTRIES_MAX 1018     /* 1024 - offsetof(php_unserialize_data, entries) / sizeof(void*) */
26#define VAR_DTOR_ENTRIES_MAX 255 /* 256 - offsetof(var_dtor_entries, data) / sizeof(zval) */
27#define VAR_ENTRIES_DBG 0
28
29/* VAR_FLAG used in var_dtor entries to signify an entry on which
30 * __wakeup/__unserialize should be called */
31#define VAR_WAKEUP_FLAG 1
32#define VAR_UNSERIALIZE_FLAG 2
33
34typedef struct {
35	zend_long used_slots;
36	void *next;
37	zval *data[VAR_ENTRIES_MAX];
38} var_entries;
39
40typedef struct {
41	zend_long used_slots;
42	void *next;
43	zval data[VAR_DTOR_ENTRIES_MAX];
44} var_dtor_entries;
45
46struct php_unserialize_data {
47	var_entries      *last;
48	var_dtor_entries *first_dtor;
49	var_dtor_entries *last_dtor;
50	HashTable        *allowed_classes;
51	HashTable        *ref_props;
52	zend_long         cur_depth;
53	zend_long         max_depth;
54	var_entries       entries;
55};
56
57PHPAPI php_unserialize_data_t php_var_unserialize_init() {
58	php_unserialize_data_t d;
59	/* fprintf(stderr, "UNSERIALIZE_INIT    == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */
60	if (BG(serialize_lock) || !BG(unserialize).level) {
61		d = emalloc(sizeof(struct php_unserialize_data));
62		d->last = &d->entries;
63		d->first_dtor = d->last_dtor = NULL;
64		d->allowed_classes = NULL;
65		d->ref_props = NULL;
66		d->cur_depth = 0;
67		d->max_depth = BG(unserialize_max_depth);
68		d->entries.used_slots = 0;
69		d->entries.next = NULL;
70		if (!BG(serialize_lock)) {
71			BG(unserialize).data = d;
72			BG(unserialize).level = 1;
73		}
74	} else {
75		d = BG(unserialize).data;
76		++BG(unserialize).level;
77	}
78	return d;
79}
80
81PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) {
82	/* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */
83	if (BG(serialize_lock) || BG(unserialize).level == 1) {
84		var_destroy(&d);
85		efree(d);
86	}
87	if (!BG(serialize_lock) && !--BG(unserialize).level) {
88		BG(unserialize).data = NULL;
89	}
90}
91
92PHPAPI HashTable *php_var_unserialize_get_allowed_classes(php_unserialize_data_t d) {
93	return d->allowed_classes;
94}
95PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) {
96	d->allowed_classes = classes;
97}
98
99PHPAPI void php_var_unserialize_set_max_depth(php_unserialize_data_t d, zend_long max_depth) {
100	d->max_depth = max_depth;
101}
102PHPAPI zend_long php_var_unserialize_get_max_depth(php_unserialize_data_t d) {
103	return d->max_depth;
104}
105
106PHPAPI void php_var_unserialize_set_cur_depth(php_unserialize_data_t d, zend_long cur_depth) {
107	d->cur_depth = cur_depth;
108}
109PHPAPI zend_long php_var_unserialize_get_cur_depth(php_unserialize_data_t d) {
110	return d->cur_depth;
111}
112
113static inline void var_push(php_unserialize_data_t *var_hashx, zval *rval)
114{
115	var_entries *var_hash = (*var_hashx)->last;
116#if VAR_ENTRIES_DBG
117	fprintf(stderr, "var_push(" ZEND_LONG_FMT "): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_P(rval));
118#endif
119
120	if (var_hash->used_slots == VAR_ENTRIES_MAX) {
121		var_hash = emalloc(sizeof(var_entries));
122		var_hash->used_slots = 0;
123		var_hash->next = 0;
124
125		(*var_hashx)->last->next = var_hash;
126		(*var_hashx)->last = var_hash;
127	}
128
129	var_hash->data[var_hash->used_slots++] = rval;
130}
131
132PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval *rval)
133{
134	if (Z_REFCOUNTED_P(rval)) {
135		zval *tmp_var = var_tmp_var(var_hashx);
136		if (!tmp_var) {
137			return;
138		}
139		ZVAL_COPY(tmp_var, rval);
140	}
141}
142
143static zend_always_inline zval *tmp_var(php_unserialize_data_t *var_hashx, zend_long num)
144{
145    var_dtor_entries *var_hash;
146	zend_long used_slots;
147
148    if (!var_hashx || !*var_hashx || num < 1) {
149        return NULL;
150    }
151
152    var_hash = (*var_hashx)->last_dtor;
153    if (!var_hash || var_hash->used_slots + num > VAR_DTOR_ENTRIES_MAX) {
154        var_hash = emalloc(sizeof(var_dtor_entries));
155        var_hash->used_slots = 0;
156        var_hash->next = 0;
157
158        if (!(*var_hashx)->first_dtor) {
159            (*var_hashx)->first_dtor = var_hash;
160        } else {
161            (*var_hashx)->last_dtor->next = var_hash;
162        }
163
164        (*var_hashx)->last_dtor = var_hash;
165    }
166	for (used_slots = var_hash->used_slots; var_hash->used_slots < used_slots + num; var_hash->used_slots++) {
167		ZVAL_UNDEF(&var_hash->data[var_hash->used_slots]);
168		Z_EXTRA(var_hash->data[var_hash->used_slots]) = 0;
169	}
170    return &var_hash->data[used_slots];
171}
172
173PHPAPI zval *var_tmp_var(php_unserialize_data_t *var_hashx)
174{
175    return tmp_var(var_hashx, 1);
176}
177
178PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval *nzval)
179{
180	zend_long i;
181	var_entries *var_hash = &(*var_hashx)->entries;
182#if VAR_ENTRIES_DBG
183	fprintf(stderr, "var_replace(" ZEND_LONG_FMT "): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_P(nzval));
184#endif
185
186	while (var_hash) {
187		for (i = 0; i < var_hash->used_slots; i++) {
188			if (var_hash->data[i] == ozval) {
189				var_hash->data[i] = nzval;
190				/* do not break here */
191			}
192		}
193		var_hash = var_hash->next;
194	}
195}
196
197static zval *var_access(php_unserialize_data_t *var_hashx, zend_long id)
198{
199	var_entries *var_hash = &(*var_hashx)->entries;
200#if VAR_ENTRIES_DBG
201	fprintf(stderr, "var_access(" ZEND_LONG_FMT "): " ZEND_LONG_FMT "\n", var_hash?var_hash->used_slots:-1L, id);
202#endif
203
204	while (id >= VAR_ENTRIES_MAX && var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) {
205		var_hash = var_hash->next;
206		id -= VAR_ENTRIES_MAX;
207	}
208
209	if (!var_hash) return NULL;
210
211	if (id < 0 || id >= var_hash->used_slots) return NULL;
212
213	return var_hash->data[id];
214}
215
216PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
217{
218	void *next;
219	zend_long i;
220	var_entries *var_hash = (*var_hashx)->entries.next;
221	var_dtor_entries *var_dtor_hash = (*var_hashx)->first_dtor;
222	zend_bool delayed_call_failed = 0;
223	zval wakeup_name, unserialize_name;
224	ZVAL_UNDEF(&wakeup_name);
225	ZVAL_UNDEF(&unserialize_name);
226
227#if VAR_ENTRIES_DBG
228	fprintf(stderr, "var_destroy( " ZEND_LONG_FMT ")\n", var_hash?var_hash->used_slots:-1L);
229#endif
230
231	while (var_hash) {
232		next = var_hash->next;
233		efree_size(var_hash, sizeof(var_entries));
234		var_hash = next;
235	}
236
237	while (var_dtor_hash) {
238		for (i = 0; i < var_dtor_hash->used_slots; i++) {
239			zval *zv = &var_dtor_hash->data[i];
240#if VAR_ENTRIES_DBG
241			fprintf(stderr, "var_destroy dtor(%p, %ld)\n", &var_dtor_hash->data[i], Z_REFCOUNT_P(&var_dtor_hash->data[i]));
242#endif
243
244			if (Z_EXTRA_P(zv) == VAR_WAKEUP_FLAG) {
245				/* Perform delayed __wakeup calls */
246				if (!delayed_call_failed) {
247					zval retval;
248					if (Z_ISUNDEF(wakeup_name)) {
249						ZVAL_STRINGL(&wakeup_name, "__wakeup", sizeof("__wakeup") - 1);
250					}
251
252					BG(serialize_lock)++;
253					if (call_user_function(NULL, zv, &wakeup_name, &retval, 0, 0) == FAILURE || Z_ISUNDEF(retval)) {
254						delayed_call_failed = 1;
255						GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
256					}
257					BG(serialize_lock)--;
258
259					zval_ptr_dtor(&retval);
260				} else {
261					GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
262				}
263			} else if (Z_EXTRA_P(zv) == VAR_UNSERIALIZE_FLAG) {
264				/* Perform delayed __unserialize calls */
265				if (!delayed_call_failed) {
266					zval retval, param;
267					ZVAL_COPY(&param, &var_dtor_hash->data[i + 1]);
268
269					if (Z_ISUNDEF(unserialize_name)) {
270						ZVAL_STRINGL(&unserialize_name, "__unserialize", sizeof("__unserialize") - 1);
271					}
272
273					BG(serialize_lock)++;
274					if (call_user_function(CG(function_table), zv, &unserialize_name, &retval, 1, &param) == FAILURE || Z_ISUNDEF(retval)) {
275						delayed_call_failed = 1;
276						GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
277					}
278					BG(serialize_lock)--;
279
280					zval_ptr_dtor(&param);
281					zval_ptr_dtor(&retval);
282				} else {
283					GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
284				}
285			}
286
287			i_zval_ptr_dtor(zv);
288		}
289		next = var_dtor_hash->next;
290		efree_size(var_dtor_hash, sizeof(var_dtor_entries));
291		var_dtor_hash = next;
292	}
293
294	zval_ptr_dtor_nogc(&wakeup_name);
295	zval_ptr_dtor_nogc(&unserialize_name);
296
297	if ((*var_hashx)->ref_props) {
298		zend_hash_destroy((*var_hashx)->ref_props);
299		FREE_HASHTABLE((*var_hashx)->ref_props);
300	}
301}
302
303/* }}} */
304
305static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t maxlen)
306{
307	size_t i, j;
308	zend_string *str = zend_string_safe_alloc(1, len, 0, 0);
309	unsigned char *end = *(unsigned char **)p+maxlen;
310
311	if (end < *p) {
312		zend_string_efree(str);
313		return NULL;
314	}
315
316	for (i = 0; i < len; i++) {
317		if (*p >= end) {
318			zend_string_efree(str);
319			return NULL;
320		}
321		if (**p != '\\') {
322			ZSTR_VAL(str)[i] = (char)**p;
323		} else {
324			unsigned char ch = 0;
325
326			for (j = 0; j < 2; j++) {
327				(*p)++;
328				if (**p >= '0' && **p <= '9') {
329					ch = (ch << 4) + (**p -'0');
330				} else if (**p >= 'a' && **p <= 'f') {
331					ch = (ch << 4) + (**p -'a'+10);
332				} else if (**p >= 'A' && **p <= 'F') {
333					ch = (ch << 4) + (**p -'A'+10);
334				} else {
335					zend_string_efree(str);
336					return NULL;
337				}
338			}
339			ZSTR_VAL(str)[i] = (char)ch;
340		}
341		(*p)++;
342	}
343	ZSTR_VAL(str)[i] = 0;
344	ZSTR_LEN(str) = i;
345	return str;
346}
347
348static inline int unserialize_allowed_class(
349		zend_string *class_name, php_unserialize_data_t *var_hashx)
350{
351	HashTable *classes = (*var_hashx)->allowed_classes;
352	zend_string *lcname;
353	int res;
354	ALLOCA_FLAG(use_heap)
355
356	if(classes == NULL) {
357		return 1;
358	}
359	if(!zend_hash_num_elements(classes)) {
360		return 0;
361	}
362
363	ZSTR_ALLOCA_ALLOC(lcname, ZSTR_LEN(class_name), use_heap);
364	zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(class_name), ZSTR_LEN(class_name));
365	res = zend_hash_exists(classes, lcname);
366	ZSTR_ALLOCA_FREE(lcname, use_heap);
367	return res;
368}
369
370#define YYFILL(n) do { } while (0)
371#define YYCTYPE unsigned char
372#define YYCURSOR cursor
373#define YYLIMIT limit
374#define YYMARKER marker
375
376
377/*!re2c
378uiv = [0-9]+;
379iv = [+-]? [0-9]+;
380nv = [+-]? ([0-9]* "." [0-9]+|[0-9]+ "." [0-9]*);
381nvexp = (iv | nv) [eE] iv;
382any = [\000-\377];
383object = [OC];
384*/
385
386
387
388static inline zend_long parse_iv2(const unsigned char *p, const unsigned char **q)
389{
390	zend_ulong result = 0;
391	zend_ulong neg = 0;
392	const unsigned char *start;
393
394	if (*p == '-') {
395		neg = 1;
396		p++;
397	} else if (UNEXPECTED(*p == '+')) {
398		p++;
399	}
400
401	while (UNEXPECTED(*p == '0')) {
402		p++;
403	}
404
405	start = p;
406
407	while (*p >= '0' && *p <= '9') {
408		result = result * 10 + ((zend_ulong)(*p) - '0');
409		p++;
410	}
411
412	if (q) {
413		*q = p;
414	}
415
416	/* number too long or overflow */
417	if (UNEXPECTED(p - start > MAX_LENGTH_OF_LONG - 1)
418	 || (SIZEOF_ZEND_LONG == 4
419	 	&& UNEXPECTED(p - start == MAX_LENGTH_OF_LONG - 1)
420	 	&& UNEXPECTED(*start > '2'))
421	 || UNEXPECTED(result > ZEND_LONG_MAX + neg)) {
422		php_error_docref(NULL, E_WARNING, "Numerical result out of range");
423		return (!neg) ? ZEND_LONG_MAX : ZEND_LONG_MIN;
424	}
425
426	return (zend_long) ((!neg) ? result : -result);
427}
428
429static inline zend_long parse_iv(const unsigned char *p)
430{
431	return parse_iv2(p, NULL);
432}
433
434/* no need to check for length - re2c already did */
435static inline size_t parse_uiv(const unsigned char *p)
436{
437	unsigned char cursor;
438	size_t result = 0;
439
440	while (1) {
441		cursor = *p;
442		if (cursor >= '0' && cursor <= '9') {
443			result = result * 10 + (size_t)(cursor - (unsigned char)'0');
444		} else {
445			break;
446		}
447		p++;
448	}
449	return result;
450}
451
452#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash
453#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash
454
455static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key);
456
457static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, zend_object *obj)
458{
459	if (var_hash) {
460		if ((*var_hash)->max_depth > 0 && (*var_hash)->cur_depth >= (*var_hash)->max_depth) {
461			php_error_docref(NULL, E_WARNING,
462				"Maximum depth of " ZEND_LONG_FMT " exceeded. "
463				"The depth limit can be changed using the max_depth unserialize() option "
464				"or the unserialize_max_depth ini setting",
465				(*var_hash)->max_depth);
466			return 0;
467		}
468		(*var_hash)->cur_depth++;
469	}
470
471	while (elements-- > 0) {
472		zval key, *data, d, *old_data;
473		zend_ulong idx;
474		zend_property_info *info = NULL;
475
476		ZVAL_UNDEF(&key);
477
478		if (!php_var_unserialize_internal(&key, p, max, NULL, 1)) {
479			zval_ptr_dtor(&key);
480			goto failure;
481		}
482
483		data = NULL;
484		ZVAL_UNDEF(&d);
485
486		if (!obj) {
487			if (Z_TYPE(key) == IS_LONG) {
488				idx = Z_LVAL(key);
489numeric_key:
490				if (UNEXPECTED((old_data = zend_hash_index_find(ht, idx)) != NULL)) {
491					//??? update hash
492					var_push_dtor(var_hash, old_data);
493					data = zend_hash_index_update(ht, idx, &d);
494				} else {
495					data = zend_hash_index_add_new(ht, idx, &d);
496				}
497			} else if (Z_TYPE(key) == IS_STRING) {
498				if (UNEXPECTED(ZEND_HANDLE_NUMERIC(Z_STR(key), idx))) {
499					goto numeric_key;
500				}
501				if (UNEXPECTED((old_data = zend_hash_find(ht, Z_STR(key))) != NULL)) {
502					//??? update hash
503					var_push_dtor(var_hash, old_data);
504					data = zend_hash_update(ht, Z_STR(key), &d);
505				} else {
506					data = zend_hash_add_new(ht, Z_STR(key), &d);
507				}
508			} else {
509				zval_ptr_dtor(&key);
510				goto failure;
511			}
512		} else {
513			if (EXPECTED(Z_TYPE(key) == IS_STRING)) {
514string_key:
515				if (obj && zend_hash_num_elements(&obj->ce->properties_info) > 0) {
516					zend_property_info *existing_propinfo;
517					zend_string *new_key;
518					const char *unmangled_class = NULL;
519					const char *unmangled_prop;
520					size_t unmangled_prop_len;
521					zend_string *unmangled;
522
523					if (UNEXPECTED(zend_unmangle_property_name_ex(Z_STR(key), &unmangled_class, &unmangled_prop, &unmangled_prop_len) == FAILURE)) {
524						zval_ptr_dtor(&key);
525						goto failure;
526					}
527
528					unmangled = zend_string_init(unmangled_prop, unmangled_prop_len, 0);
529
530					existing_propinfo = zend_hash_find_ptr(&obj->ce->properties_info, unmangled);
531                    if ((unmangled_class == NULL || !strcmp(unmangled_class, "*") || !strcasecmp(unmangled_class, ZSTR_VAL(obj->ce->name)))
532                            && (existing_propinfo != NULL)
533							&& (existing_propinfo->flags & ZEND_ACC_PPP_MASK)) {
534						if (existing_propinfo->flags & ZEND_ACC_PROTECTED) {
535							new_key = zend_mangle_property_name(
536								"*", 1, ZSTR_VAL(unmangled), ZSTR_LEN(unmangled), 0);
537							zend_string_release_ex(unmangled, 0);
538						} else if (existing_propinfo->flags & ZEND_ACC_PRIVATE) {
539							if (unmangled_class != NULL && strcmp(unmangled_class, "*") != 0) {
540								new_key = zend_mangle_property_name(
541									unmangled_class, strlen(unmangled_class),
542										ZSTR_VAL(unmangled), ZSTR_LEN(unmangled),
543										0);
544							} else {
545								new_key = zend_mangle_property_name(
546									ZSTR_VAL(existing_propinfo->ce->name), ZSTR_LEN(existing_propinfo->ce->name),
547									ZSTR_VAL(unmangled), ZSTR_LEN(unmangled),
548									0);
549							}
550							zend_string_release_ex(unmangled, 0);
551						} else {
552							ZEND_ASSERT(existing_propinfo->flags & ZEND_ACC_PUBLIC);
553							new_key = unmangled;
554						}
555						zval_ptr_dtor_str(&key);
556						ZVAL_STR(&key, new_key);
557					} else {
558						zend_string_release_ex(unmangled, 0);
559					}
560				}
561
562				if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
563					if (Z_TYPE_P(old_data) == IS_INDIRECT) {
564						old_data = Z_INDIRECT_P(old_data);
565						info = zend_get_typed_property_info_for_slot(obj, old_data);
566						var_push_dtor(var_hash, old_data);
567						data = zend_hash_update_ind(ht, Z_STR(key), &d);
568
569						if (UNEXPECTED(info)) {
570							/* Remember to which property this slot belongs, so we can add a
571							 * type source if it is turned into a reference lateron. */
572							if (!(*var_hash)->ref_props) {
573								(*var_hash)->ref_props = emalloc(sizeof(HashTable));
574								zend_hash_init((*var_hash)->ref_props, 8, NULL, NULL, 0);
575							}
576							zend_hash_index_update_ptr(
577								(*var_hash)->ref_props, (zend_uintptr_t) data, info);
578						}
579					} else {
580						var_push_dtor(var_hash, old_data);
581						data = zend_hash_update_ind(ht, Z_STR(key), &d);
582					}
583				} else {
584					data = zend_hash_add_new(ht, Z_STR(key), &d);
585				}
586			} else if (Z_TYPE(key) == IS_LONG) {
587				/* object properties should include no integers */
588				convert_to_string(&key);
589				goto string_key;
590			} else {
591				zval_ptr_dtor(&key);
592				goto failure;
593			}
594		}
595
596		if (!php_var_unserialize_internal(data, p, max, var_hash, 0)) {
597			zval_ptr_dtor(&key);
598			goto failure;
599		}
600
601		if (UNEXPECTED(info)) {
602			if (!zend_verify_prop_assignable_by_ref(info, data, /* strict */ 1)) {
603				zval_ptr_dtor(data);
604				ZVAL_UNDEF(data);
605				zval_dtor(&key);
606				goto failure;
607			}
608			if (Z_ISREF_P(data)) {
609				ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), info);
610			}
611		}
612
613		if (BG(unserialize).level > 1) {
614			var_push_dtor(var_hash, data);
615		}
616		zval_ptr_dtor_str(&key);
617
618		if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
619			(*p)--;
620			goto failure;
621		}
622	}
623
624	if (var_hash) {
625		(*var_hash)->cur_depth--;
626	}
627	return 1;
628
629failure:
630	if (var_hash) {
631		(*var_hash)->cur_depth--;
632	}
633	return 0;
634}
635
636static inline int finish_nested_data(UNSERIALIZE_PARAMETER)
637{
638	if (*p >= max || **p != '}') {
639		return 0;
640	}
641
642	(*p)++;
643	return 1;
644}
645
646static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
647{
648	zend_long datalen;
649
650	datalen = parse_iv2((*p) + 2, p);
651
652	(*p) += 2;
653
654	if (datalen < 0 || (max - (*p)) <= datalen) {
655		zend_error(E_WARNING, "Insufficient data for unserializing - " ZEND_LONG_FMT " required, " ZEND_LONG_FMT " present", datalen, (zend_long)(max - (*p)));
656		return 0;
657	}
658
659	/* Check that '}' is present before calling ce->unserialize() to mitigate issues
660	 * with unserialize reading past the end of the passed buffer if the string is not
661	 * appropriately terminated (usually NUL terminated, but '}' is also sufficient.) */
662	if ((*p)[datalen] != '}') {
663		return 0;
664	}
665
666	if (ce->unserialize == NULL) {
667		zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name));
668		object_init_ex(rval, ce);
669	} else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) {
670		return 0;
671	}
672
673	(*p) += datalen + 1; /* +1 for '}' */
674	return 1;
675}
676
677#ifdef PHP_WIN32
678# pragma optimize("", off)
679#endif
680static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, zend_bool has_unserialize)
681{
682	HashTable *ht;
683	zend_bool has_wakeup;
684
685	if (has_unserialize) {
686		zval ary, *tmp;
687
688		if (elements >= HT_MAX_SIZE) {
689			return 0;
690		}
691
692		array_init_size(&ary, elements);
693		/* Avoid reallocation due to packed -> mixed conversion. */
694		zend_hash_real_init_mixed(Z_ARRVAL(ary));
695		if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL(ary), elements, NULL)) {
696			ZVAL_DEREF(rval);
697			GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
698			zval_ptr_dtor(&ary);
699			return 0;
700		}
701
702		/* Delay __unserialize() call until end of serialization. We use two slots here to
703		 * store both the object and the unserialized data array. */
704		ZVAL_DEREF(rval);
705		tmp = tmp_var(var_hash, 2);
706		ZVAL_COPY(tmp, rval);
707		Z_EXTRA_P(tmp) = VAR_UNSERIALIZE_FLAG;
708		tmp++;
709		ZVAL_COPY_VALUE(tmp, &ary);
710
711		return finish_nested_data(UNSERIALIZE_PASSTHRU);
712	}
713
714	has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY
715		&& zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1);
716
717	ht = Z_OBJPROP_P(rval);
718	if (elements >= (zend_long)(HT_MAX_SIZE - zend_hash_num_elements(ht))) {
719		return 0;
720	}
721
722	zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, HT_FLAGS(ht) & HASH_FLAG_PACKED);
723	if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJ_P(rval))) {
724		if (has_wakeup) {
725			ZVAL_DEREF(rval);
726			GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
727		}
728		return 0;
729	}
730
731	ZVAL_DEREF(rval);
732	if (has_wakeup) {
733		/* Delay __wakeup call until end of serialization */
734		zval *wakeup_var = var_tmp_var(var_hash);
735		ZVAL_COPY(wakeup_var, rval);
736		Z_EXTRA_P(wakeup_var) = VAR_WAKEUP_FLAG;
737	}
738
739	return finish_nested_data(UNSERIALIZE_PASSTHRU);
740}
741#ifdef PHP_WIN32
742# pragma optimize("", on)
743#endif
744
745PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
746{
747	var_entries *orig_var_entries = (*var_hash)->last;
748	zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0;
749	int result;
750
751	result = php_var_unserialize_internal(UNSERIALIZE_PASSTHRU, 0);
752
753	if (!result) {
754		/* If the unserialization failed, mark all elements that have been added to var_hash
755		 * as NULL. This will forbid their use by other unserialize() calls in the same
756		 * unserialization context. */
757		var_entries *e = orig_var_entries;
758		zend_long s = orig_used_slots;
759		while (e) {
760			for (; s < e->used_slots; s++) {
761				e->data[s] = NULL;
762			}
763
764			e = e->next;
765			s = 0;
766		}
767	}
768
769	return result;
770}
771
772static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key)
773{
774	const unsigned char *cursor, *limit, *marker, *start;
775	zval *rval_ref;
776
777	limit = max;
778	cursor = *p;
779
780	if (YYCURSOR >= YYLIMIT) {
781		return 0;
782	}
783
784	if (var_hash && (*p)[0] != 'R') {
785		var_push(var_hash, rval);
786	}
787
788	start = cursor;
789
790/*!re2c
791
792"R:" uiv ";"		{
793	zend_long id;
794
795 	*p = YYCURSOR;
796	if (!var_hash) return 0;
797
798	id = parse_uiv(start + 2) - 1;
799	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
800		return 0;
801	}
802
803	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
804		return 0;
805	}
806
807	if (!Z_ISREF_P(rval_ref)) {
808		zend_property_info *info = NULL;
809		if ((*var_hash)->ref_props) {
810			info = zend_hash_index_find_ptr((*var_hash)->ref_props, (zend_uintptr_t) rval_ref);
811		}
812		ZVAL_NEW_REF(rval_ref, rval_ref);
813		if (info) {
814			ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(rval_ref), info);
815		}
816	}
817
818	ZVAL_COPY(rval, rval_ref);
819
820	return 1;
821}
822
823"r:" uiv ";"		{
824	zend_long id;
825
826 	*p = YYCURSOR;
827	if (!var_hash) return 0;
828
829	id = parse_uiv(start + 2) - 1;
830	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
831		return 0;
832	}
833
834	if (rval_ref == rval) {
835		return 0;
836	}
837
838	ZVAL_DEREF(rval_ref);
839	if (Z_TYPE_P(rval_ref) != IS_OBJECT) {
840		return 0;
841	}
842
843	ZVAL_COPY(rval, rval_ref);
844
845	return 1;
846}
847
848"N;"	{
849	*p = YYCURSOR;
850	ZVAL_NULL(rval);
851	return 1;
852}
853
854"b:0;"	{
855	*p = YYCURSOR;
856	ZVAL_FALSE(rval);
857	return 1;
858}
859
860"b:1;"	{
861	*p = YYCURSOR;
862	ZVAL_TRUE(rval);
863	return 1;
864}
865
866"i:" iv ";"	{
867#if SIZEOF_ZEND_LONG == 4
868	int digits = YYCURSOR - start - 3;
869
870	if (start[2] == '-' || start[2] == '+') {
871		digits--;
872	}
873
874	/* Use double for large zend_long values that were serialized on a 64-bit system */
875	if (digits >= MAX_LENGTH_OF_LONG - 1) {
876		if (digits == MAX_LENGTH_OF_LONG - 1) {
877			int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1);
878
879			if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) {
880				goto use_double;
881			}
882		} else {
883			goto use_double;
884		}
885	}
886#endif
887	*p = YYCURSOR;
888	ZVAL_LONG(rval, parse_iv(start + 2));
889	return 1;
890}
891
892"d:" ("NAN" | "-"? "INF") ";"	{
893	*p = YYCURSOR;
894
895	if (!strncmp((char*)start + 2, "NAN", 3)) {
896		ZVAL_DOUBLE(rval, ZEND_NAN);
897	} else if (!strncmp((char*)start + 2, "INF", 3)) {
898		ZVAL_DOUBLE(rval, ZEND_INFINITY);
899	} else if (!strncmp((char*)start + 2, "-INF", 4)) {
900		ZVAL_DOUBLE(rval, -ZEND_INFINITY);
901	} else {
902		ZVAL_NULL(rval);
903	}
904
905	return 1;
906}
907
908"d:" (iv | nv | nvexp) ";"	{
909#if SIZEOF_ZEND_LONG == 4
910use_double:
911#endif
912	*p = YYCURSOR;
913	ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
914	return 1;
915}
916
917"s:" uiv ":" ["] 	{
918	size_t len, maxlen;
919	char *str;
920
921	len = parse_uiv(start + 2);
922	maxlen = max - YYCURSOR;
923	if (maxlen < len) {
924		*p = start + 2;
925		return 0;
926	}
927
928	str = (char*)YYCURSOR;
929
930	YYCURSOR += len;
931
932	if (*(YYCURSOR) != '"') {
933		*p = YYCURSOR;
934		return 0;
935	}
936
937	if (*(YYCURSOR + 1) != ';') {
938		*p = YYCURSOR + 1;
939		return 0;
940	}
941
942	YYCURSOR += 2;
943	*p = YYCURSOR;
944
945	if (len == 0) {
946		ZVAL_EMPTY_STRING(rval);
947	} else if (len == 1) {
948		ZVAL_INTERNED_STR(rval, ZSTR_CHAR((zend_uchar)*str));
949	} else if (as_key) {
950		ZVAL_STR(rval, zend_string_init_interned(str, len, 0));
951	} else {
952		ZVAL_STRINGL(rval, str, len);
953	}
954	return 1;
955}
956
957"S:" uiv ":" ["] 	{
958	size_t len, maxlen;
959	zend_string *str;
960
961	len = parse_uiv(start + 2);
962	maxlen = max - YYCURSOR;
963	if (maxlen < len) {
964		*p = start + 2;
965		return 0;
966	}
967
968	if ((str = unserialize_str(&YYCURSOR, len, maxlen)) == NULL) {
969		return 0;
970	}
971
972	if (*(YYCURSOR) != '"') {
973		zend_string_efree(str);
974		*p = YYCURSOR;
975		return 0;
976	}
977
978	if (*(YYCURSOR + 1) != ';') {
979		efree(str);
980		*p = YYCURSOR + 1;
981		return 0;
982	}
983
984	YYCURSOR += 2;
985	*p = YYCURSOR;
986
987	ZVAL_STR(rval, str);
988	return 1;
989}
990
991"a:" uiv ":" "{" {
992	zend_long elements = parse_iv(start + 2);
993	/* use iv() not uiv() in order to check data range */
994	*p = YYCURSOR;
995    if (!var_hash) return 0;
996
997	if (elements < 0 || elements >= HT_MAX_SIZE || elements > max - YYCURSOR) {
998		return 0;
999	}
1000
1001	if (elements) {
1002		array_init_size(rval, elements);
1003		/* we can't convert from packed to hash during unserialization, because
1004		   reference to some zvals might be keept in var_hash (to support references) */
1005		zend_hash_real_init_mixed(Z_ARRVAL_P(rval));
1006	} else {
1007		ZVAL_EMPTY_ARRAY(rval);
1008		return finish_nested_data(UNSERIALIZE_PASSTHRU);
1009	}
1010
1011	/* The array may contain references to itself, in which case we'll be modifying an
1012	 * rc>1 array. This is okay, since the array is, ostensibly, only visible to
1013	 * unserialize (in practice unserialization handlers also see it). Ideally we should
1014	 * prohibit "r:" references to non-objects, as we only generate them for objects. */
1015	HT_ALLOW_COW_VIOLATION(Z_ARRVAL_P(rval));
1016
1017	if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, NULL)) {
1018		return 0;
1019	}
1020
1021	return finish_nested_data(UNSERIALIZE_PASSTHRU);
1022}
1023
1024object ":" uiv ":" ["]	{
1025	size_t len, len2, len3, maxlen;
1026	zend_long elements;
1027	char *str;
1028	zend_string *class_name;
1029	zend_class_entry *ce;
1030	zend_bool incomplete_class = 0;
1031	zend_bool custom_object = 0;
1032	zend_bool has_unserialize = 0;
1033
1034	zval user_func;
1035	zval retval;
1036	zval args[1];
1037
1038    if (!var_hash) return 0;
1039	if (*start == 'C') {
1040		custom_object = 1;
1041	}
1042
1043	len2 = len = parse_uiv(start + 2);
1044	maxlen = max - YYCURSOR;
1045	if (maxlen < len || len == 0) {
1046		*p = start + 2;
1047		return 0;
1048	}
1049
1050	str = (char*)YYCURSOR;
1051
1052	YYCURSOR += len;
1053
1054	if (*(YYCURSOR) != '"') {
1055		*p = YYCURSOR;
1056		return 0;
1057	}
1058	if (*(YYCURSOR+1) != ':') {
1059		*p = YYCURSOR+1;
1060		return 0;
1061	}
1062
1063	len3 = strspn(str, "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\");
1064	if (len3 != len)
1065	{
1066		*p = YYCURSOR + len3 - len;
1067		return 0;
1068	}
1069
1070	class_name = zend_string_init(str, len, 0);
1071
1072	do {
1073		if(!unserialize_allowed_class(class_name, var_hash)) {
1074			incomplete_class = 1;
1075			ce = PHP_IC_ENTRY;
1076			break;
1077		}
1078
1079		/* Try to find class directly */
1080		BG(serialize_lock)++;
1081		ce = zend_lookup_class(class_name);
1082		if (ce) {
1083			BG(serialize_lock)--;
1084			if (EG(exception)) {
1085				zend_string_release_ex(class_name, 0);
1086				return 0;
1087			}
1088			break;
1089		}
1090		BG(serialize_lock)--;
1091
1092		if (EG(exception)) {
1093			zend_string_release_ex(class_name, 0);
1094			return 0;
1095		}
1096
1097		/* Check for unserialize callback */
1098		if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) {
1099			incomplete_class = 1;
1100			ce = PHP_IC_ENTRY;
1101			break;
1102		}
1103
1104		/* Call unserialize callback */
1105		ZVAL_STRING(&user_func, PG(unserialize_callback_func));
1106
1107		ZVAL_STR_COPY(&args[0], class_name);
1108		BG(serialize_lock)++;
1109		if (call_user_function_ex(NULL, NULL, &user_func, &retval, 1, args, 0, NULL) != SUCCESS) {
1110			BG(serialize_lock)--;
1111			if (EG(exception)) {
1112				zend_string_release_ex(class_name, 0);
1113				zval_ptr_dtor(&user_func);
1114				zval_ptr_dtor(&args[0]);
1115				return 0;
1116			}
1117			php_error_docref(NULL, E_WARNING, "defined (%s) but not found", Z_STRVAL(user_func));
1118			incomplete_class = 1;
1119			ce = PHP_IC_ENTRY;
1120			zval_ptr_dtor(&user_func);
1121			zval_ptr_dtor(&args[0]);
1122			break;
1123		}
1124		BG(serialize_lock)--;
1125		zval_ptr_dtor(&retval);
1126		if (EG(exception)) {
1127			zend_string_release_ex(class_name, 0);
1128			zval_ptr_dtor(&user_func);
1129			zval_ptr_dtor(&args[0]);
1130			return 0;
1131		}
1132
1133		/* The callback function may have defined the class */
1134		BG(serialize_lock)++;
1135		if ((ce = zend_lookup_class(class_name)) == NULL) {
1136			php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", Z_STRVAL(user_func));
1137			incomplete_class = 1;
1138			ce = PHP_IC_ENTRY;
1139		}
1140		BG(serialize_lock)--;
1141
1142		zval_ptr_dtor(&user_func);
1143		zval_ptr_dtor(&args[0]);
1144		break;
1145	} while (1);
1146
1147	*p = YYCURSOR;
1148
1149	if (custom_object) {
1150		int ret;
1151
1152		ret = object_custom(UNSERIALIZE_PASSTHRU, ce);
1153
1154		if (ret && incomplete_class) {
1155			php_store_class_name(rval, ZSTR_VAL(class_name), len2);
1156		}
1157		zend_string_release_ex(class_name, 0);
1158		return ret;
1159	}
1160
1161	if (*p >= max - 2) {
1162		zend_error(E_WARNING, "Bad unserialize data");
1163		zend_string_release_ex(class_name, 0);
1164		return 0;
1165	}
1166
1167	elements = parse_iv2(*p + 2, p);
1168	if (elements < 0 || elements > max - YYCURSOR) {
1169		zend_string_release_ex(class_name, 0);
1170		return 0;
1171	}
1172
1173	*p += 2;
1174
1175	has_unserialize = !incomplete_class
1176		&& zend_hash_str_exists(&ce->function_table, "__unserialize", sizeof("__unserialize")-1);
1177
1178	/* If this class implements Serializable, it should not land here but in object_custom().
1179	 * The passed string obviously doesn't descend from the regular serializer. However, if
1180	 * there is both Serializable::unserialize() and __unserialize(), then both may be used,
1181	 * depending on the serialization format. */
1182	if (ce->serialize != NULL && !has_unserialize) {
1183		zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ZSTR_VAL(ce->name));
1184		zend_string_release_ex(class_name, 0);
1185		return 0;
1186	}
1187
1188	if (object_init_ex(rval, ce) == FAILURE) {
1189		zend_string_release_ex(class_name, 0);
1190		return 0;
1191	}
1192
1193	if (incomplete_class) {
1194		php_store_class_name(rval, ZSTR_VAL(class_name), len2);
1195	}
1196	zend_string_release_ex(class_name, 0);
1197
1198	return object_common(UNSERIALIZE_PASSTHRU, elements, has_unserialize);
1199}
1200
1201"}" {
1202	/* this is the case where we have less data than planned */
1203	php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data");
1204	return 0; /* not sure if it should be 0 or 1 here? */
1205}
1206
1207any	{ return 0; }
1208
1209*/
1210
1211	return 0;
1212}
1213