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