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