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