xref: /PHP-7.1/ext/standard/var_unserializer.re (revision c793885b)
1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 7                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2018 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/* $Id$ */
20
21#include "php.h"
22#include "ext/standard/php_var.h"
23#include "php_incomplete_class.h"
24
25struct php_unserialize_data {
26	void *first;
27	void *last;
28	void *first_dtor;
29	void *last_dtor;
30	HashTable *allowed_classes;
31};
32
33PHPAPI php_unserialize_data_t php_var_unserialize_init() {
34	php_unserialize_data_t d;
35	/* fprintf(stderr, "UNSERIALIZE_INIT    == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */
36	if (BG(serialize_lock) || !BG(unserialize).level) {
37		d = ecalloc(1, sizeof(struct php_unserialize_data));
38		if (!BG(serialize_lock)) {
39			BG(unserialize).data = d;
40			BG(unserialize).level = 1;
41		}
42	} else {
43		d = BG(unserialize).data;
44		++BG(unserialize).level;
45	}
46	return d;
47}
48
49PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) {
50	/* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */
51	if (BG(serialize_lock) || BG(unserialize).level == 1) {
52		var_destroy(&d);
53		efree(d);
54	}
55	if (!BG(serialize_lock) && !--BG(unserialize).level) {
56		BG(unserialize).data = NULL;
57	}
58}
59
60PHPAPI HashTable *php_var_unserialize_get_allowed_classes(php_unserialize_data_t d) {
61	return d->allowed_classes;
62}
63PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) {
64	d->allowed_classes = classes;
65}
66
67
68/* {{{ reference-handling for unserializer: var_* */
69#define VAR_ENTRIES_MAX 1024
70#define VAR_ENTRIES_DBG 0
71
72/* VAR_FLAG used in var_dtor entries to signify an entry on which __wakeup should be called */
73#define VAR_WAKEUP_FLAG 1
74
75typedef struct {
76	zval *data[VAR_ENTRIES_MAX];
77	zend_long used_slots;
78	void *next;
79} var_entries;
80
81typedef struct {
82	zval data[VAR_ENTRIES_MAX];
83	zend_long used_slots;
84	void *next;
85} var_dtor_entries;
86
87static inline void var_push(php_unserialize_data_t *var_hashx, zval *rval)
88{
89	var_entries *var_hash = (*var_hashx)->last;
90#if VAR_ENTRIES_DBG
91	fprintf(stderr, "var_push(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_P(rval));
92#endif
93
94	if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
95		var_hash = emalloc(sizeof(var_entries));
96		var_hash->used_slots = 0;
97		var_hash->next = 0;
98
99		if (!(*var_hashx)->first) {
100			(*var_hashx)->first = var_hash;
101		} else {
102			((var_entries *) (*var_hashx)->last)->next = var_hash;
103		}
104
105		(*var_hashx)->last = var_hash;
106	}
107
108	var_hash->data[var_hash->used_slots++] = rval;
109}
110
111PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval *rval)
112{
113	zval *tmp_var = var_tmp_var(var_hashx);
114    if (!tmp_var) {
115        return;
116    }
117	ZVAL_COPY(tmp_var, rval);
118}
119
120PHPAPI zval *var_tmp_var(php_unserialize_data_t *var_hashx)
121{
122    var_dtor_entries *var_hash;
123
124    if (!var_hashx || !*var_hashx) {
125        return NULL;
126    }
127
128    var_hash = (*var_hashx)->last_dtor;
129    if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
130        var_hash = emalloc(sizeof(var_dtor_entries));
131        var_hash->used_slots = 0;
132        var_hash->next = 0;
133
134        if (!(*var_hashx)->first_dtor) {
135            (*var_hashx)->first_dtor = var_hash;
136        } else {
137            ((var_dtor_entries *) (*var_hashx)->last_dtor)->next = var_hash;
138        }
139
140        (*var_hashx)->last_dtor = var_hash;
141    }
142    ZVAL_UNDEF(&var_hash->data[var_hash->used_slots]);
143	Z_EXTRA(var_hash->data[var_hash->used_slots]) = 0;
144    return &var_hash->data[var_hash->used_slots++];
145}
146
147PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval *nzval)
148{
149	zend_long i;
150	var_entries *var_hash = (*var_hashx)->first;
151#if VAR_ENTRIES_DBG
152	fprintf(stderr, "var_replace(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_P(nzval));
153#endif
154
155	while (var_hash) {
156		for (i = 0; i < var_hash->used_slots; i++) {
157			if (var_hash->data[i] == ozval) {
158				var_hash->data[i] = nzval;
159				/* do not break here */
160			}
161		}
162		var_hash = var_hash->next;
163	}
164}
165
166static zval *var_access(php_unserialize_data_t *var_hashx, zend_long id)
167{
168	var_entries *var_hash = (*var_hashx)->first;
169#if VAR_ENTRIES_DBG
170	fprintf(stderr, "var_access(%ld): %ld\n", var_hash?var_hash->used_slots:-1L, id);
171#endif
172
173	while (id >= VAR_ENTRIES_MAX && var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) {
174		var_hash = var_hash->next;
175		id -= VAR_ENTRIES_MAX;
176	}
177
178	if (!var_hash) return NULL;
179
180	if (id < 0 || id >= var_hash->used_slots) return NULL;
181
182	return var_hash->data[id];
183}
184
185PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
186{
187	void *next;
188	zend_long i;
189	var_entries *var_hash = (*var_hashx)->first;
190	var_dtor_entries *var_dtor_hash = (*var_hashx)->first_dtor;
191	zend_bool wakeup_failed = 0;
192	zval wakeup_name;
193	ZVAL_UNDEF(&wakeup_name);
194
195#if VAR_ENTRIES_DBG
196	fprintf(stderr, "var_destroy(%ld)\n", var_hash?var_hash->used_slots:-1L);
197#endif
198
199	while (var_hash) {
200		next = var_hash->next;
201		efree_size(var_hash, sizeof(var_entries));
202		var_hash = next;
203	}
204
205	while (var_dtor_hash) {
206		for (i = 0; i < var_dtor_hash->used_slots; i++) {
207			zval *zv = &var_dtor_hash->data[i];
208#if VAR_ENTRIES_DBG
209			fprintf(stderr, "var_destroy dtor(%p, %ld)\n", var_dtor_hash->data[i], Z_REFCOUNT_P(var_dtor_hash->data[i]));
210#endif
211
212			/* Perform delayed __wakeup calls */
213			if (Z_EXTRA_P(zv) == VAR_WAKEUP_FLAG) {
214				if (!wakeup_failed) {
215					zval retval;
216					if (Z_ISUNDEF(wakeup_name)) {
217						ZVAL_STRINGL(&wakeup_name, "__wakeup", sizeof("__wakeup") - 1);
218					}
219
220					BG(serialize_lock)++;
221					if (call_user_function_ex(CG(function_table), zv, &wakeup_name, &retval, 0, 0, 1, NULL) == FAILURE || Z_ISUNDEF(retval)) {
222						wakeup_failed = 1;
223						GC_FLAGS(Z_OBJ_P(zv)) |= IS_OBJ_DESTRUCTOR_CALLED;
224					}
225					BG(serialize_lock)--;
226
227					zval_ptr_dtor(&retval);
228				} else {
229					GC_FLAGS(Z_OBJ_P(zv)) |= IS_OBJ_DESTRUCTOR_CALLED;
230				}
231			}
232
233			zval_ptr_dtor(zv);
234		}
235		next = var_dtor_hash->next;
236		efree_size(var_dtor_hash, sizeof(var_dtor_entries));
237		var_dtor_hash = next;
238	}
239
240	zval_ptr_dtor(&wakeup_name);
241}
242
243/* }}} */
244
245static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t maxlen)
246{
247	size_t i, j;
248	zend_string *str = zend_string_safe_alloc(1, len, 0, 0);
249	unsigned char *end = *(unsigned char **)p+maxlen;
250
251	if (end < *p) {
252		zend_string_free(str);
253		return NULL;
254	}
255
256	for (i = 0; i < len; i++) {
257		if (*p >= end) {
258			zend_string_free(str);
259			return NULL;
260		}
261		if (**p != '\\') {
262			ZSTR_VAL(str)[i] = (char)**p;
263		} else {
264			unsigned char ch = 0;
265
266			for (j = 0; j < 2; j++) {
267				(*p)++;
268				if (**p >= '0' && **p <= '9') {
269					ch = (ch << 4) + (**p -'0');
270				} else if (**p >= 'a' && **p <= 'f') {
271					ch = (ch << 4) + (**p -'a'+10);
272				} else if (**p >= 'A' && **p <= 'F') {
273					ch = (ch << 4) + (**p -'A'+10);
274				} else {
275					zend_string_free(str);
276					return NULL;
277				}
278			}
279			ZSTR_VAL(str)[i] = (char)ch;
280		}
281		(*p)++;
282	}
283	ZSTR_VAL(str)[i] = 0;
284	ZSTR_LEN(str) = i;
285	return str;
286}
287
288static inline int unserialize_allowed_class(
289		zend_string *class_name, php_unserialize_data_t *var_hashx)
290{
291	HashTable *classes = (*var_hashx)->allowed_classes;
292	zend_string *lcname;
293	int res;
294	ALLOCA_FLAG(use_heap)
295
296	if(classes == NULL) {
297		return 1;
298	}
299	if(!zend_hash_num_elements(classes)) {
300		return 0;
301	}
302
303	ZSTR_ALLOCA_ALLOC(lcname, ZSTR_LEN(class_name), use_heap);
304	zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(class_name), ZSTR_LEN(class_name));
305	res = zend_hash_exists(classes, lcname);
306	ZSTR_ALLOCA_FREE(lcname, use_heap);
307	return res;
308}
309
310#define YYFILL(n) do { } while (0)
311#define YYCTYPE unsigned char
312#define YYCURSOR cursor
313#define YYLIMIT limit
314#define YYMARKER marker
315
316
317/*!re2c
318uiv = [+]? [0-9]+;
319iv = [+-]? [0-9]+;
320nv = [+-]? ([0-9]* "." [0-9]+|[0-9]+ "." [0-9]*);
321nvexp = (iv | nv) [eE] [+-]? iv;
322any = [\000-\377];
323object = [OC];
324*/
325
326
327
328static inline zend_long parse_iv2(const unsigned char *p, const unsigned char **q)
329{
330	zend_long result = 0;
331	char *end;
332
333	errno = 0;
334	result = ZEND_STRTOL((const char*)p, &end, 0);
335
336	if (q) {
337		*q = (const unsigned char *)end;
338	}
339
340	if (errno) {
341		php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
342		return result;
343	}
344
345	return result;
346}
347
348static inline zend_long parse_iv(const unsigned char *p)
349{
350	return parse_iv2(p, NULL);
351}
352
353/* no need to check for length - re2c already did */
354static inline size_t parse_uiv(const unsigned char *p)
355{
356	unsigned char cursor;
357	size_t result = 0;
358
359	if (*p == '+') {
360		p++;
361	}
362
363	while (1) {
364		cursor = *p;
365		if (cursor >= '0' && cursor <= '9') {
366			result = result * 10 + (size_t)(cursor - (unsigned char)'0');
367		} else {
368			break;
369		}
370		p++;
371	}
372	return result;
373}
374
375#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash
376#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash
377
378static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER);
379
380static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops)
381{
382	while (elements-- > 0) {
383		zval key, *data, d, *old_data;
384		zend_ulong idx;
385
386		ZVAL_UNDEF(&key);
387
388		if (!php_var_unserialize_internal(&key, p, max, NULL)) {
389			zval_dtor(&key);
390			return 0;
391		}
392
393		data = NULL;
394		ZVAL_UNDEF(&d);
395
396		if (!objprops) {
397			if (Z_TYPE(key) == IS_LONG) {
398				idx = Z_LVAL(key);
399numeric_key:
400				if (UNEXPECTED((old_data = zend_hash_index_find(ht, idx)) != NULL)) {
401					//??? update hash
402					var_push_dtor(var_hash, old_data);
403					data = zend_hash_index_update(ht, idx, &d);
404				} else {
405					data = zend_hash_index_add_new(ht, idx, &d);
406				}
407			} else if (Z_TYPE(key) == IS_STRING) {
408				if (UNEXPECTED(ZEND_HANDLE_NUMERIC(Z_STR(key), idx))) {
409					goto numeric_key;
410				}
411				if (UNEXPECTED((old_data = zend_hash_find(ht, Z_STR(key))) != NULL)) {
412					//??? update hash
413					var_push_dtor(var_hash, old_data);
414					data = zend_hash_update(ht, Z_STR(key), &d);
415				} else {
416					data = zend_hash_add_new(ht, Z_STR(key), &d);
417				}
418			} else {
419				zval_dtor(&key);
420				return 0;
421			}
422		} else {
423			if (EXPECTED(Z_TYPE(key) == IS_STRING)) {
424string_key:
425				if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
426					if (Z_TYPE_P(old_data) == IS_INDIRECT) {
427						old_data = Z_INDIRECT_P(old_data);
428					}
429					var_push_dtor(var_hash, old_data);
430					data = zend_hash_update_ind(ht, Z_STR(key), &d);
431				} else {
432					data = zend_hash_add_new(ht, Z_STR(key), &d);
433				}
434			} else if (Z_TYPE(key) == IS_LONG) {
435				/* object properties should include no integers */
436				convert_to_string(&key);
437				goto string_key;
438			} else {
439				zval_dtor(&key);
440				return 0;
441			}
442		}
443
444		if (!php_var_unserialize_internal(data, p, max, var_hash)) {
445			zval_dtor(&key);
446			return 0;
447		}
448
449		var_push_dtor(var_hash, data);
450		zval_dtor(&key);
451
452		if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
453			(*p)--;
454			return 0;
455		}
456	}
457
458	return 1;
459}
460
461static inline int finish_nested_data(UNSERIALIZE_PARAMETER)
462{
463	if (*p >= max || **p != '}') {
464		return 0;
465	}
466
467	(*p)++;
468	return 1;
469}
470
471static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
472{
473	zend_long datalen;
474
475	datalen = parse_iv2((*p) + 2, p);
476
477	(*p) += 2;
478
479	if (datalen < 0 || (max - (*p)) <= datalen) {
480		zend_error(E_WARNING, "Insufficient data for unserializing - " ZEND_LONG_FMT " required, " ZEND_LONG_FMT " present", datalen, (zend_long)(max - (*p)));
481		return 0;
482	}
483
484	/* Check that '}' is present before calling ce->unserialize() to mitigate issues
485	 * with unserialize reading past the end of the passed buffer if the string is not
486	 * appropriately terminated (usually NUL terminated, but '}' is also sufficient.) */
487	if ((*p)[datalen] != '}') {
488		return 0;
489	}
490
491	if (ce->unserialize == NULL) {
492		zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name));
493		object_init_ex(rval, ce);
494	} else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) {
495		return 0;
496	}
497
498	(*p) += datalen + 1; /* +1 for '}' */
499	return 1;
500}
501
502static inline zend_long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
503{
504	zend_long elements;
505
506	if( *p >= max - 2) {
507		zend_error(E_WARNING, "Bad unserialize data");
508		return -1;
509	}
510
511	elements = parse_iv2((*p) + 2, p);
512
513	(*p) += 2;
514
515	if (ce->serialize == NULL) {
516		object_init_ex(rval, ce);
517	} else {
518		/* If this class implements Serializable, it should not land here but in object_custom(). The passed string
519		obviously doesn't descend from the regular serializer. */
520		zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ZSTR_VAL(ce->name));
521		return -1;
522	}
523
524	return elements;
525}
526
527#ifdef PHP_WIN32
528# pragma optimize("", off)
529#endif
530static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
531{
532	HashTable *ht;
533	zend_bool has_wakeup;
534
535	if (Z_TYPE_P(rval) != IS_OBJECT) {
536		return 0;
537	}
538
539	has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY
540		&& zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1);
541
542	ht = Z_OBJPROP_P(rval);
543	if (elements >= HT_MAX_SIZE - zend_hash_num_elements(ht)) {
544		return 0;
545	}
546
547	zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED));
548	if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) {
549		if (has_wakeup) {
550			ZVAL_DEREF(rval);
551			GC_FLAGS(Z_OBJ_P(rval)) |= IS_OBJ_DESTRUCTOR_CALLED;
552		}
553		return 0;
554	}
555
556	ZVAL_DEREF(rval);
557	if (has_wakeup) {
558		/* Delay __wakeup call until end of serialization */
559		zval *wakeup_var = var_tmp_var(var_hash);
560		ZVAL_COPY(wakeup_var, rval);
561		Z_EXTRA_P(wakeup_var) = VAR_WAKEUP_FLAG;
562	}
563
564	return finish_nested_data(UNSERIALIZE_PASSTHRU);
565}
566#ifdef PHP_WIN32
567# pragma optimize("", on)
568#endif
569
570PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
571{
572	var_entries *orig_var_entries = (*var_hash)->last;
573	zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0;
574	int result;
575
576	result = php_var_unserialize_internal(UNSERIALIZE_PASSTHRU);
577
578	if (!result) {
579		/* If the unserialization failed, mark all elements that have been added to var_hash
580		 * as NULL. This will forbid their use by other unserialize() calls in the same
581		 * unserialization context. */
582		var_entries *e = orig_var_entries;
583		zend_long s = orig_used_slots;
584		while (e) {
585			for (; s < e->used_slots; s++) {
586				e->data[s] = NULL;
587			}
588
589			e = e->next;
590			s = 0;
591		}
592	}
593
594	return result;
595}
596
597static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
598{
599	const unsigned char *cursor, *limit, *marker, *start;
600	zval *rval_ref;
601
602	limit = max;
603	cursor = *p;
604
605	if (YYCURSOR >= YYLIMIT) {
606		return 0;
607	}
608
609	if (var_hash && (*p)[0] != 'R') {
610		var_push(var_hash, rval);
611	}
612
613	start = cursor;
614
615/*!re2c
616
617"R:" iv ";"		{
618	zend_long id;
619
620 	*p = YYCURSOR;
621	if (!var_hash) return 0;
622
623	id = parse_iv(start + 2) - 1;
624	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
625		return 0;
626	}
627
628	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
629		return 0;
630	}
631
632	if (Z_ISREF_P(rval_ref)) {
633		ZVAL_COPY(rval, rval_ref);
634	} else {
635		ZVAL_NEW_REF(rval_ref, rval_ref);
636		ZVAL_COPY(rval, rval_ref);
637	}
638
639	return 1;
640}
641
642"r:" iv ";"		{
643	zend_long id;
644
645 	*p = YYCURSOR;
646	if (!var_hash) return 0;
647
648	id = parse_iv(start + 2) - 1;
649	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
650		return 0;
651	}
652
653	if (rval_ref == rval) {
654		return 0;
655	}
656
657	if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
658		return 0;
659	}
660
661	ZVAL_COPY(rval, rval_ref);
662
663	return 1;
664}
665
666"N;"	{
667	*p = YYCURSOR;
668	ZVAL_NULL(rval);
669	return 1;
670}
671
672"b:" [01] ";"	{
673	*p = YYCURSOR;
674	ZVAL_BOOL(rval, parse_iv(start + 2));
675	return 1;
676}
677
678"i:" iv ";"	{
679#if SIZEOF_ZEND_LONG == 4
680	int digits = YYCURSOR - start - 3;
681
682	if (start[2] == '-' || start[2] == '+') {
683		digits--;
684	}
685
686	/* Use double for large zend_long values that were serialized on a 64-bit system */
687	if (digits >= MAX_LENGTH_OF_LONG - 1) {
688		if (digits == MAX_LENGTH_OF_LONG - 1) {
689			int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1);
690
691			if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) {
692				goto use_double;
693			}
694		} else {
695			goto use_double;
696		}
697	}
698#endif
699	*p = YYCURSOR;
700	ZVAL_LONG(rval, parse_iv(start + 2));
701	return 1;
702}
703
704"d:" ("NAN" | "-"? "INF") ";"	{
705	*p = YYCURSOR;
706
707	if (!strncmp((char*)start + 2, "NAN", 3)) {
708		ZVAL_DOUBLE(rval, php_get_nan());
709	} else if (!strncmp((char*)start + 2, "INF", 3)) {
710		ZVAL_DOUBLE(rval, php_get_inf());
711	} else if (!strncmp((char*)start + 2, "-INF", 4)) {
712		ZVAL_DOUBLE(rval, -php_get_inf());
713	} else {
714		ZVAL_NULL(rval);
715	}
716
717	return 1;
718}
719
720"d:" (iv | nv | nvexp) ";"	{
721#if SIZEOF_ZEND_LONG == 4
722use_double:
723#endif
724	*p = YYCURSOR;
725	ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
726	return 1;
727}
728
729"s:" uiv ":" ["] 	{
730	size_t len, maxlen;
731	char *str;
732
733	len = parse_uiv(start + 2);
734	maxlen = max - YYCURSOR;
735	if (maxlen < len) {
736		*p = start + 2;
737		return 0;
738	}
739
740	str = (char*)YYCURSOR;
741
742	YYCURSOR += len;
743
744	if (*(YYCURSOR) != '"') {
745		*p = YYCURSOR;
746		return 0;
747	}
748
749	if (*(YYCURSOR + 1) != ';') {
750		*p = YYCURSOR + 1;
751		return 0;
752	}
753
754	YYCURSOR += 2;
755	*p = YYCURSOR;
756
757	ZVAL_STRINGL(rval, str, len);
758	return 1;
759}
760
761"S:" uiv ":" ["] 	{
762	size_t len, maxlen;
763	zend_string *str;
764
765	len = parse_uiv(start + 2);
766	maxlen = max - YYCURSOR;
767	if (maxlen < len) {
768		*p = start + 2;
769		return 0;
770	}
771
772	if ((str = unserialize_str(&YYCURSOR, len, maxlen)) == NULL) {
773		return 0;
774	}
775
776	if (*(YYCURSOR) != '"') {
777		zend_string_free(str);
778		*p = YYCURSOR;
779		return 0;
780	}
781
782	if (*(YYCURSOR + 1) != ';') {
783		efree(str);
784		*p = YYCURSOR + 1;
785		return 0;
786	}
787
788	YYCURSOR += 2;
789	*p = YYCURSOR;
790
791	ZVAL_STR(rval, str);
792	return 1;
793}
794
795"a:" uiv ":" "{" {
796	zend_long elements = parse_iv(start + 2);
797	/* use iv() not uiv() in order to check data range */
798	*p = YYCURSOR;
799    if (!var_hash) return 0;
800
801	if (elements < 0 || elements >= HT_MAX_SIZE) {
802		return 0;
803	}
804
805	array_init_size(rval, elements);
806	if (elements) {
807		/* we can't convert from packed to hash during unserialization, because
808		   reference to some zvals might be keept in var_hash (to support references) */
809		zend_hash_real_init(Z_ARRVAL_P(rval), 0);
810	}
811
812	if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) {
813		return 0;
814	}
815
816	return finish_nested_data(UNSERIALIZE_PASSTHRU);
817}
818
819"o:" iv ":" ["] {
820	zend_long elements;
821    if (!var_hash) return 0;
822
823	elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR);
824	if (elements < 0 || elements >= HT_MAX_SIZE) {
825		return 0;
826	}
827	return object_common2(UNSERIALIZE_PASSTHRU, elements);
828}
829
830object ":" uiv ":" ["]	{
831	size_t len, len2, len3, maxlen;
832	zend_long elements;
833	char *str;
834	zend_string *class_name;
835	zend_class_entry *ce;
836	int incomplete_class = 0;
837
838	int custom_object = 0;
839
840	zval user_func;
841	zval retval;
842	zval args[1];
843
844    if (!var_hash) return 0;
845	if (*start == 'C') {
846		custom_object = 1;
847	}
848
849	len2 = len = parse_uiv(start + 2);
850	maxlen = max - YYCURSOR;
851	if (maxlen < len || len == 0) {
852		*p = start + 2;
853		return 0;
854	}
855
856	str = (char*)YYCURSOR;
857
858	YYCURSOR += len;
859
860	if (*(YYCURSOR) != '"') {
861		*p = YYCURSOR;
862		return 0;
863	}
864	if (*(YYCURSOR+1) != ':') {
865		*p = YYCURSOR+1;
866		return 0;
867	}
868
869	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\\");
870	if (len3 != len)
871	{
872		*p = YYCURSOR + len3 - len;
873		return 0;
874	}
875
876	class_name = zend_string_init(str, len, 0);
877
878	do {
879		if(!unserialize_allowed_class(class_name, var_hash)) {
880			incomplete_class = 1;
881			ce = PHP_IC_ENTRY;
882			break;
883		}
884
885		/* Try to find class directly */
886		BG(serialize_lock)++;
887		ce = zend_lookup_class(class_name);
888		if (ce) {
889			BG(serialize_lock)--;
890			if (EG(exception)) {
891				zend_string_release(class_name);
892				return 0;
893			}
894			break;
895		}
896		BG(serialize_lock)--;
897
898		if (EG(exception)) {
899			zend_string_release(class_name);
900			return 0;
901		}
902
903		/* Check for unserialize callback */
904		if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) {
905			incomplete_class = 1;
906			ce = PHP_IC_ENTRY;
907			break;
908		}
909
910		/* Call unserialize callback */
911		ZVAL_STRING(&user_func, PG(unserialize_callback_func));
912
913		ZVAL_STR_COPY(&args[0], class_name);
914		BG(serialize_lock)++;
915		if (call_user_function_ex(CG(function_table), NULL, &user_func, &retval, 1, args, 0, NULL) != SUCCESS) {
916			BG(serialize_lock)--;
917			if (EG(exception)) {
918				zend_string_release(class_name);
919				zval_ptr_dtor(&user_func);
920				zval_ptr_dtor(&args[0]);
921				return 0;
922			}
923			php_error_docref(NULL, E_WARNING, "defined (%s) but not found", Z_STRVAL(user_func));
924			incomplete_class = 1;
925			ce = PHP_IC_ENTRY;
926			zval_ptr_dtor(&user_func);
927			zval_ptr_dtor(&args[0]);
928			break;
929		}
930		BG(serialize_lock)--;
931		zval_ptr_dtor(&retval);
932		if (EG(exception)) {
933			zend_string_release(class_name);
934			zval_ptr_dtor(&user_func);
935			zval_ptr_dtor(&args[0]);
936			return 0;
937		}
938
939		/* The callback function may have defined the class */
940		BG(serialize_lock)++;
941		if ((ce = zend_lookup_class(class_name)) == NULL) {
942			php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", Z_STRVAL(user_func));
943			incomplete_class = 1;
944			ce = PHP_IC_ENTRY;
945		}
946		BG(serialize_lock)--;
947
948		zval_ptr_dtor(&user_func);
949		zval_ptr_dtor(&args[0]);
950		break;
951	} while (1);
952
953	*p = YYCURSOR;
954
955	if (custom_object) {
956		int ret;
957
958		ret = object_custom(UNSERIALIZE_PASSTHRU, ce);
959
960		if (ret && incomplete_class) {
961			php_store_class_name(rval, ZSTR_VAL(class_name), len2);
962		}
963		zend_string_release(class_name);
964		return ret;
965	}
966
967	elements = object_common1(UNSERIALIZE_PASSTHRU, ce);
968
969	if (elements < 0) {
970	   zend_string_release(class_name);
971	   return 0;
972	}
973
974	if (incomplete_class) {
975		php_store_class_name(rval, ZSTR_VAL(class_name), len2);
976	}
977	zend_string_release(class_name);
978
979	return object_common2(UNSERIALIZE_PASSTHRU, elements);
980}
981
982"}" {
983	/* this is the case where we have less data than planned */
984	php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data");
985	return 0; /* not sure if it should be 0 or 1 here? */
986}
987
988any	{ return 0; }
989
990*/
991
992	return 0;
993}
994