xref: /php-src/ext/standard/var_unserializer.re (revision 5ca72eca)
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	for (i = 0; i < len; i++) {
330		if (*p >= end) {
331			zend_string_efree(str);
332			return NULL;
333		}
334		if (**p != '\\') {
335			ZSTR_VAL(str)[i] = (char)**p;
336		} else {
337			unsigned char ch = 0;
338
339			for (j = 0; j < 2; j++) {
340				(*p)++;
341				if (**p >= '0' && **p <= '9') {
342					ch = (ch << 4) + (**p -'0');
343				} else if (**p >= 'a' && **p <= 'f') {
344					ch = (ch << 4) + (**p -'a'+10);
345				} else if (**p >= 'A' && **p <= 'F') {
346					ch = (ch << 4) + (**p -'A'+10);
347				} else {
348					zend_string_efree(str);
349					return NULL;
350				}
351			}
352			ZSTR_VAL(str)[i] = (char)ch;
353		}
354		(*p)++;
355	}
356	ZSTR_VAL(str)[i] = 0;
357	ZSTR_LEN(str) = i;
358	return str;
359}
360
361static inline int unserialize_allowed_class(
362		zend_string *lcname, php_unserialize_data_t *var_hashx)
363{
364	HashTable *classes = (*var_hashx)->allowed_classes;
365
366	if(classes == NULL) {
367		return 1;
368	}
369	if(!zend_hash_num_elements(classes)) {
370		return 0;
371	}
372
373	return zend_hash_exists(classes, lcname);
374}
375
376#define YYFILL(n) do { } while (0)
377#define YYCTYPE unsigned char
378#define YYCURSOR cursor
379#define YYLIMIT limit
380#define YYMARKER marker
381
382
383/*!re2c
384uiv = [0-9]+;
385iv = [+-]? [0-9]+;
386nv = [+-]? ([0-9]* "." [0-9]+|[0-9]+ "." [0-9]*);
387nvexp = (iv | nv) [eE] iv;
388any = [\000-\377];
389object = [OC];
390*/
391
392
393
394static inline zend_long parse_iv2(const unsigned char *p, const unsigned char **q)
395{
396	zend_ulong result = 0;
397	zend_ulong neg = 0;
398	const unsigned char *start;
399
400	if (*p == '-') {
401		neg = 1;
402		p++;
403	} else if (UNEXPECTED(*p == '+')) {
404		p++;
405	}
406
407	while (UNEXPECTED(*p == '0')) {
408		p++;
409	}
410
411	start = p;
412
413	while (*p >= '0' && *p <= '9') {
414		result = result * 10 + ((zend_ulong)(*p) - '0');
415		p++;
416	}
417
418	if (q) {
419		*q = p;
420	}
421
422	/* number too long or overflow */
423	if (UNEXPECTED(p - start > MAX_LENGTH_OF_LONG - 1)
424	 || (SIZEOF_ZEND_LONG == 4
425	 	&& UNEXPECTED(p - start == MAX_LENGTH_OF_LONG - 1)
426	 	&& UNEXPECTED(*start > '2'))
427	 || UNEXPECTED(result > ZEND_LONG_MAX + neg)) {
428		php_error_docref(NULL, E_WARNING, "Numerical result out of range");
429		return (!neg) ? ZEND_LONG_MAX : ZEND_LONG_MIN;
430	}
431
432	return (zend_long) ((!neg) ? result : -result);
433}
434
435static inline zend_long parse_iv(const unsigned char *p)
436{
437	return parse_iv2(p, NULL);
438}
439
440/* no need to check for length - re2c already did */
441static inline size_t parse_uiv(const unsigned char *p)
442{
443	unsigned char cursor;
444	size_t result = 0;
445
446	while (1) {
447		cursor = *p;
448		if (cursor >= '0' && cursor <= '9') {
449			result = result * 10 + (size_t)(cursor - (unsigned char)'0');
450		} else {
451			break;
452		}
453		p++;
454	}
455	return result;
456}
457
458#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash
459#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash
460
461static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER);
462
463static zend_always_inline int process_nested_array_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements)
464{
465	if (var_hash) {
466		if ((*var_hash)->max_depth > 0 && (*var_hash)->cur_depth >= (*var_hash)->max_depth) {
467			php_error_docref(NULL, E_WARNING,
468				"Maximum depth of " ZEND_LONG_FMT " exceeded. "
469				"The depth limit can be changed using the max_depth unserialize() option "
470				"or the unserialize_max_depth ini setting",
471				(*var_hash)->max_depth);
472			return 0;
473		}
474		(*var_hash)->cur_depth++;
475	}
476
477	while (elements-- > 0) {
478		zval key, *data;
479		zend_ulong idx;
480
481		ZVAL_UNDEF(&key);
482
483		if (!php_var_unserialize_internal(&key, p, max, NULL)) {
484			zval_ptr_dtor(&key);
485			goto failure;
486		}
487
488		if (Z_TYPE(key) == IS_LONG) {
489			idx = Z_LVAL(key);
490numeric_key:
491			data = zend_hash_index_lookup(ht, idx);
492			if (UNEXPECTED(Z_TYPE_INFO_P(data) != IS_NULL)) {
493				var_push_dtor_value(var_hash, data);
494				ZVAL_NULL(data);
495			}
496		} else if (Z_TYPE(key) == IS_STRING) {
497			if (UNEXPECTED(ZEND_HANDLE_NUMERIC(Z_STR(key), idx))) {
498				zval_ptr_dtor_str(&key);
499				goto numeric_key;
500			}
501			data = zend_hash_lookup(ht, Z_STR(key));
502			if (UNEXPECTED(Z_TYPE_INFO_P(data) != IS_NULL)) {
503				var_push_dtor_value(var_hash, data);
504				ZVAL_NULL(data);
505			}
506			zval_ptr_dtor_str(&key);
507		} else {
508			zval_ptr_dtor(&key);
509			goto failure;
510		}
511
512		if (!php_var_unserialize_internal(data, p, max, var_hash)) {
513			goto failure;
514		}
515
516		if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
517			(*p)--;
518			goto failure;
519		}
520	}
521
522	if (var_hash) {
523		(*var_hash)->cur_depth--;
524	}
525	return 1;
526
527failure:
528	if (var_hash) {
529		(*var_hash)->cur_depth--;
530	}
531	return 0;
532}
533
534static int is_property_visibility_changed(zend_class_entry *ce, zval *key)
535{
536	if (zend_hash_num_elements(&ce->properties_info) > 0) {
537		zend_property_info *existing_propinfo;
538		const char *unmangled_class = NULL;
539		const char *unmangled_prop;
540		size_t unmangled_prop_len;
541
542		if (UNEXPECTED(zend_unmangle_property_name_ex(Z_STR_P(key), &unmangled_class, &unmangled_prop, &unmangled_prop_len) == FAILURE)) {
543			zval_ptr_dtor_str(key);
544			return -1;
545		}
546
547		if (unmangled_class == NULL) {
548			existing_propinfo = zend_hash_find_ptr(&ce->properties_info, Z_STR_P(key));
549			if (existing_propinfo != NULL) {
550				zval_ptr_dtor_str(key);
551				ZVAL_STR_COPY(key, existing_propinfo->name);
552				return 1;
553			}
554		} else {
555			if (!strcmp(unmangled_class, "*")
556			 || !strcasecmp(unmangled_class, ZSTR_VAL(ce->name))) {
557				existing_propinfo = zend_hash_str_find_ptr(
558					&ce->properties_info, unmangled_prop, unmangled_prop_len);
559				if (existing_propinfo != NULL) {
560					zval_ptr_dtor_str(key);
561					ZVAL_STR_COPY(key, existing_propinfo->name);
562					return 1;
563				}
564			}
565		}
566	}
567	return 0;
568}
569
570
571static zend_always_inline int process_nested_object_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, zend_object *obj)
572{
573	if (var_hash) {
574		if ((*var_hash)->max_depth > 0 && (*var_hash)->cur_depth >= (*var_hash)->max_depth) {
575			php_error_docref(NULL, E_WARNING,
576				"Maximum depth of " ZEND_LONG_FMT " exceeded. "
577				"The depth limit can be changed using the max_depth unserialize() option "
578				"or the unserialize_max_depth ini setting",
579				(*var_hash)->max_depth);
580			return 0;
581		}
582		(*var_hash)->cur_depth++;
583	}
584
585	while (elements-- > 0) {
586		zval key, *data;
587		zend_property_info *info = NULL;
588
589		ZVAL_UNDEF(&key);
590
591		if (!php_var_unserialize_internal(&key, p, max, NULL)) {
592			zval_ptr_dtor(&key);
593			goto failure;
594		}
595
596		if (EXPECTED(Z_TYPE(key) == IS_STRING)) {
597string_key:
598			data = zend_hash_find(ht, Z_STR(key));
599			if (data != NULL) {
600				if (Z_TYPE_P(data) == IS_INDIRECT) {
601declared_property:
602					/* This is a property with a declaration */
603					data = Z_INDIRECT_P(data);
604					info = zend_get_typed_property_info_for_slot(obj, data);
605					if (info) {
606						if (Z_ISREF_P(data)) {
607							/* If the value is overwritten, remove old type source from ref. */
608							ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(data), info);
609						}
610
611						if ((*var_hash)->ref_props) {
612							/* Remove old entry from ref_props table, if it exists. */
613							zend_hash_index_del(
614								(*var_hash)->ref_props, (uintptr_t) data);
615						}
616					}
617					/* We may override default property value, but they are usually immutable */
618					if (Z_REFCOUNTED_P(data)) {
619						var_push_dtor_value(var_hash, data);
620					}
621					ZVAL_NULL(data);
622				} else {
623					/* Unusual override of dynamic property */
624					int ret = is_property_visibility_changed(obj->ce, &key);
625
626					if (ret > 0) {
627						goto second_try;
628					} else if (!ret) {
629						var_push_dtor_value(var_hash, data);
630						ZVAL_NULL(data);
631					} else if (ret < 0) {
632						goto failure;
633					}
634				}
635			} else {
636				int ret = is_property_visibility_changed(obj->ce, &key);
637
638				if (EXPECTED(!ret)) {
639					if (UNEXPECTED(obj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) {
640						zend_throw_error(NULL, "Cannot create dynamic property %s::$%s",
641							ZSTR_VAL(obj->ce->name), zend_get_unmangled_property_name(Z_STR_P(&key)));
642						zval_ptr_dtor_str(&key);
643						goto failure;
644					} else if (!(obj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) {
645						zend_error(E_DEPRECATED, "Creation of dynamic property %s::$%s is deprecated",
646							ZSTR_VAL(obj->ce->name), zend_get_unmangled_property_name(Z_STR_P(&key)));
647						if (EG(exception)) {
648							zval_ptr_dtor_str(&key);
649							goto failure;
650						}
651					}
652
653					data = zend_hash_add_new(ht, Z_STR(key), &EG(uninitialized_zval));
654				} else if (ret < 0) {
655					goto failure;
656				} else {
657second_try:
658					data = zend_hash_lookup(ht, Z_STR(key));
659					if (Z_TYPE_P(data) == IS_INDIRECT) {
660						goto declared_property;
661					} else if (UNEXPECTED(Z_TYPE_INFO_P(data) != IS_NULL)) {
662						var_push_dtor_value(var_hash, data);
663						ZVAL_NULL(data);
664					}
665				}
666			}
667			zval_ptr_dtor_str(&key);
668		} else if (Z_TYPE(key) == IS_LONG) {
669			/* object properties should include no integers */
670			convert_to_string(&key);
671			goto string_key;
672		} else {
673			zval_ptr_dtor(&key);
674			goto failure;
675		}
676
677		if (!php_var_unserialize_internal(data, p, max, var_hash)) {
678			if (info && Z_ISREF_P(data)) {
679				/* Add type source even if we failed to unserialize.
680				 * The data is still stored in the property. */
681				ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), info);
682			}
683			goto failure;
684		}
685
686		if (UNEXPECTED(info)) {
687			if (!zend_verify_prop_assignable_by_ref(info, data, /* strict */ 1)) {
688				zval_ptr_dtor(data);
689				ZVAL_UNDEF(data);
690				goto failure;
691			}
692
693			if (Z_ISREF_P(data)) {
694				ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), info);
695			} else {
696				/* Remember to which property this slot belongs, so we can add a
697				 * type source if it is turned into a reference lateron. */
698				if (!(*var_hash)->ref_props) {
699					(*var_hash)->ref_props = emalloc(sizeof(HashTable));
700					zend_hash_init((*var_hash)->ref_props, 8, NULL, NULL, 0);
701				}
702				zend_hash_index_update_ptr(
703					(*var_hash)->ref_props, (uintptr_t) data, info);
704			}
705		}
706
707		if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
708			(*p)--;
709			goto failure;
710		}
711	}
712
713	if (var_hash) {
714		(*var_hash)->cur_depth--;
715	}
716	return 1;
717
718failure:
719	if (var_hash) {
720		(*var_hash)->cur_depth--;
721	}
722	return 0;
723}
724
725static inline int finish_nested_data(UNSERIALIZE_PARAMETER)
726{
727	if (*p >= max || **p != '}') {
728		return 0;
729	}
730
731	(*p)++;
732	return 1;
733}
734
735static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
736{
737	zend_long datalen;
738
739	datalen = parse_iv2((*p) + 2, p);
740
741	if (max - (*p) < 2) {
742		return 0;
743	}
744
745	if ((*p)[0] != ':') {
746		return 0;
747	}
748
749	if ((*p)[1] != '{') {
750		(*p) += 1;
751		return 0;
752	}
753
754	(*p) += 2;
755
756	if (datalen < 0 || (max - (*p)) <= datalen) {
757		zend_error(E_WARNING, "Insufficient data for unserializing - " ZEND_LONG_FMT " required, " ZEND_LONG_FMT " present", datalen, (zend_long)(max - (*p)));
758		return 0;
759	}
760
761	/* Check that '}' is present before calling ce->unserialize() to mitigate issues
762	 * with unserialize reading past the end of the passed buffer if the string is not
763	 * appropriately terminated (usually NUL terminated, but '}' is also sufficient.) */
764	if ((*p)[datalen] != '}') {
765		(*p) += datalen;
766		return 0;
767	}
768
769	if (ce->unserialize == NULL) {
770		zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name));
771		object_init_ex(rval, ce);
772	} else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) {
773		return 0;
774	}
775
776	(*p) += datalen + 1; /* +1 for '}' */
777	return 1;
778}
779
780#ifdef PHP_WIN32
781# pragma optimize("", off)
782#endif
783static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, bool has_unserialize)
784{
785	HashTable *ht;
786	bool has_wakeup;
787
788	if (has_unserialize) {
789		zval ary, *tmp;
790
791		if (elements >= HT_MAX_SIZE) {
792			return 0;
793		}
794
795		array_init_size(&ary, elements);
796		/* Avoid reallocation due to packed -> mixed conversion. */
797		zend_hash_real_init_mixed(Z_ARRVAL(ary));
798		if (!process_nested_array_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL(ary), elements)) {
799			ZVAL_DEREF(rval);
800			GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
801			zval_ptr_dtor(&ary);
802			return 0;
803		}
804
805		/* Delay __unserialize() call until end of serialization. We use two slots here to
806		 * store both the object and the unserialized data array. */
807		ZVAL_DEREF(rval);
808		tmp = tmp_var(var_hash, 2);
809		ZVAL_COPY(tmp, rval);
810		Z_EXTRA_P(tmp) = VAR_UNSERIALIZE_FLAG;
811		tmp++;
812		ZVAL_COPY_VALUE(tmp, &ary);
813
814		return finish_nested_data(UNSERIALIZE_PASSTHRU);
815	}
816
817	has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY
818		&& zend_hash_exists(&Z_OBJCE_P(rval)->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP));
819
820	ht = Z_OBJPROP_P(rval);
821	if (elements >= (zend_long)(HT_MAX_SIZE - zend_hash_num_elements(ht))) {
822		return 0;
823	}
824
825	zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, HT_IS_PACKED(ht));
826	if (!process_nested_object_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJ_P(rval))) {
827		if (has_wakeup) {
828			ZVAL_DEREF(rval);
829			GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
830		}
831		return 0;
832	}
833
834	ZVAL_DEREF(rval);
835	if (has_wakeup) {
836		/* Delay __wakeup call until end of serialization */
837		zval *wakeup_var = var_tmp_var(var_hash);
838		ZVAL_COPY(wakeup_var, rval);
839		Z_EXTRA_P(wakeup_var) = VAR_WAKEUP_FLAG;
840	}
841
842	return finish_nested_data(UNSERIALIZE_PASSTHRU);
843}
844#ifdef PHP_WIN32
845# pragma optimize("", on)
846#endif
847
848PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
849{
850	var_entries *orig_var_entries = (*var_hash)->last;
851	zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0;
852	int result;
853
854	result = php_var_unserialize_internal(UNSERIALIZE_PASSTHRU);
855
856	if (!result) {
857		/* If the unserialization failed, mark all elements that have been added to var_hash
858		 * as NULL. This will forbid their use by other unserialize() calls in the same
859		 * unserialization context. */
860		var_entries *e = orig_var_entries;
861		zend_long s = orig_used_slots;
862		while (e) {
863			for (; s < e->used_slots; s++) {
864				e->data[s] = NULL;
865			}
866
867			e = e->next;
868			s = 0;
869		}
870	}
871
872	return result;
873}
874
875static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
876{
877	const unsigned char *cursor, *limit, *marker, *start;
878	zval *rval_ref;
879
880	limit = max;
881	cursor = *p;
882
883	if (YYCURSOR >= YYLIMIT) {
884		return 0;
885	}
886
887	if (var_hash && (*p)[0] != 'R') {
888		var_push(var_hash, rval);
889	}
890
891	start = cursor;
892
893/*!re2c
894
895"R:" uiv ";"		{
896	zend_long id;
897
898 	*p = YYCURSOR;
899	if (!var_hash) return 0;
900
901	id = parse_uiv(start + 2) - 1;
902	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
903		return 0;
904	}
905
906	if (rval_ref == rval || (Z_ISREF_P(rval_ref) && Z_REFVAL_P(rval_ref) == rval)) {
907		return 0;
908	}
909
910	if (!Z_ISREF_P(rval_ref)) {
911		zend_property_info *info = NULL;
912		if ((*var_hash)->ref_props) {
913			info = zend_hash_index_find_ptr((*var_hash)->ref_props, (uintptr_t) rval_ref);
914		}
915		ZVAL_NEW_REF(rval_ref, rval_ref);
916		if (info) {
917			ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(rval_ref), info);
918		}
919	}
920
921	ZVAL_COPY(rval, rval_ref);
922
923	return 1;
924}
925
926"r:" uiv ";"		{
927	zend_long id;
928
929 	*p = YYCURSOR;
930	if (!var_hash) return 0;
931
932	id = parse_uiv(start + 2) - 1;
933	if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
934		return 0;
935	}
936
937	if (rval_ref == rval) {
938		return 0;
939	}
940
941	ZVAL_DEREF(rval_ref);
942	if (Z_TYPE_P(rval_ref) != IS_OBJECT) {
943		return 0;
944	}
945
946	ZVAL_COPY(rval, rval_ref);
947
948	return 1;
949}
950
951"N;"	{
952	*p = YYCURSOR;
953	ZVAL_NULL(rval);
954	return 1;
955}
956
957"b:0;"	{
958	*p = YYCURSOR;
959	ZVAL_FALSE(rval);
960	return 1;
961}
962
963"b:1;"	{
964	*p = YYCURSOR;
965	ZVAL_TRUE(rval);
966	return 1;
967}
968
969"i:" iv ";"	{
970#if SIZEOF_ZEND_LONG == 4
971	int digits = YYCURSOR - start - 3;
972
973	if (start[2] == '-' || start[2] == '+') {
974		digits--;
975	}
976
977	/* Use double for large zend_long values that were serialized on a 64-bit system */
978	if (digits >= MAX_LENGTH_OF_LONG - 1) {
979		if (digits == MAX_LENGTH_OF_LONG - 1) {
980			int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1);
981
982			if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) {
983				goto use_double;
984			}
985		} else {
986			goto use_double;
987		}
988	}
989#endif
990	*p = YYCURSOR;
991	ZVAL_LONG(rval, parse_iv(start + 2));
992	return 1;
993}
994
995"d:" ("NAN" | "-"? "INF") ";"	{
996	*p = YYCURSOR;
997
998	if (!strncmp((char*)start + 2, "NAN", 3)) {
999		ZVAL_DOUBLE(rval, ZEND_NAN);
1000	} else if (!strncmp((char*)start + 2, "INF", 3)) {
1001		ZVAL_DOUBLE(rval, ZEND_INFINITY);
1002	} else if (!strncmp((char*)start + 2, "-INF", 4)) {
1003		ZVAL_DOUBLE(rval, -ZEND_INFINITY);
1004	} else {
1005		ZVAL_NULL(rval);
1006	}
1007
1008	return 1;
1009}
1010
1011"d:" (iv | nv | nvexp) ";"	{
1012#if SIZEOF_ZEND_LONG == 4
1013use_double:
1014#endif
1015	*p = YYCURSOR;
1016	ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
1017	return 1;
1018}
1019
1020"s:" uiv ":" ["] 	{
1021	size_t len, maxlen;
1022	char *str;
1023
1024	len = parse_uiv(start + 2);
1025	maxlen = max - YYCURSOR;
1026	if (maxlen < len) {
1027		*p = start + 2;
1028		return 0;
1029	}
1030
1031	str = (char*)YYCURSOR;
1032
1033	YYCURSOR += len;
1034
1035	if (*(YYCURSOR) != '"') {
1036		*p = YYCURSOR;
1037		return 0;
1038	}
1039
1040	if (*(YYCURSOR + 1) != ';') {
1041		*p = YYCURSOR + 1;
1042		return 0;
1043	}
1044
1045	YYCURSOR += 2;
1046	*p = YYCURSOR;
1047
1048	if (!var_hash) {
1049		/* Array or object key unserialization */
1050		ZVAL_STR(rval, zend_string_init_existing_interned(str, len, 0));
1051	} else {
1052		ZVAL_STRINGL_FAST(rval, str, len);
1053	}
1054	return 1;
1055}
1056
1057"S:" uiv ":" ["] 	{
1058	size_t len, maxlen;
1059	zend_string *str;
1060
1061	len = parse_uiv(start + 2);
1062	maxlen = max - YYCURSOR;
1063	if (maxlen < len) {
1064		*p = start + 2;
1065		return 0;
1066	}
1067
1068	if ((str = unserialize_str(&YYCURSOR, len, maxlen)) == NULL) {
1069		return 0;
1070	}
1071
1072	if (*(YYCURSOR) != '"') {
1073		zend_string_efree(str);
1074		*p = YYCURSOR;
1075		return 0;
1076	}
1077
1078	if (*(YYCURSOR + 1) != ';') {
1079		efree(str);
1080		*p = YYCURSOR + 1;
1081		return 0;
1082	}
1083
1084	YYCURSOR += 2;
1085	*p = YYCURSOR;
1086
1087	ZVAL_STR(rval, str);
1088	return 1;
1089}
1090
1091"a:" uiv ":" "{" {
1092	zend_long elements = parse_iv(start + 2);
1093	/* use iv() not uiv() in order to check data range */
1094	*p = YYCURSOR;
1095    if (!var_hash) return 0;
1096
1097	if (elements < 0 || elements >= HT_MAX_SIZE || IS_FAKE_ELEM_COUNT(elements, max - YYCURSOR)) {
1098		return 0;
1099	}
1100
1101	if (elements) {
1102		array_init_size(rval, elements);
1103		/* we can't convert from packed to hash during unserialization, because
1104		   reference to some zvals might be kept in var_hash (to support references) */
1105		zend_hash_real_init_mixed(Z_ARRVAL_P(rval));
1106	} else {
1107		ZVAL_EMPTY_ARRAY(rval);
1108		return finish_nested_data(UNSERIALIZE_PASSTHRU);
1109	}
1110
1111	/* The array may contain references to itself, in which case we'll be modifying an
1112	 * rc>1 array. This is okay, since the array is, ostensibly, only visible to
1113	 * unserialize (in practice unserialization handlers also see it). Ideally we should
1114	 * prohibit "r:" references to non-objects, as we only generate them for objects. */
1115	HT_ALLOW_COW_VIOLATION(Z_ARRVAL_P(rval));
1116
1117	if (!process_nested_array_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements)) {
1118		return 0;
1119	}
1120
1121	return finish_nested_data(UNSERIALIZE_PASSTHRU);
1122}
1123
1124object ":" uiv ":" ["]	{
1125	size_t len, maxlen;
1126	zend_long elements;
1127	char *str;
1128	zend_string *class_name;
1129	zend_class_entry *ce;
1130	bool incomplete_class = 0;
1131	bool custom_object = 0;
1132	bool has_unserialize = 0;
1133
1134	zval user_func;
1135	zval retval;
1136	zval args[1];
1137
1138    if (!var_hash) return 0;
1139	if (*start == 'C') {
1140		custom_object = 1;
1141	}
1142
1143	len = parse_uiv(start + 2);
1144	maxlen = max - YYCURSOR;
1145	if (maxlen < len || len == 0) {
1146		*p = start + 2;
1147		return 0;
1148	}
1149
1150	str = (char*)YYCURSOR;
1151
1152	YYCURSOR += len;
1153
1154	if (*(YYCURSOR) != '"') {
1155		*p = YYCURSOR;
1156		return 0;
1157	}
1158	if (*(YYCURSOR+1) != ':') {
1159		*p = YYCURSOR+1;
1160		return 0;
1161	}
1162
1163	if (len == 0) {
1164		/* empty class names are not allowed */
1165		return 0;
1166	}
1167
1168	if (str[0] == '\000') {
1169		/* runtime definition keys are not allowed */
1170		return 0;
1171	}
1172
1173	if (str[0] == '\\') {
1174		/* class name can't start from namespace separator */
1175		return 0;
1176	}
1177
1178	class_name = zend_string_init_interned(str, len, 0);
1179
1180	do {
1181		zend_string *lc_name;
1182
1183		if (!(*var_hash)->allowed_classes && ZSTR_HAS_CE_CACHE(class_name)) {
1184			ce = ZSTR_GET_CE_CACHE(class_name);
1185			if (ce) {
1186				break;
1187			}
1188		}
1189
1190		lc_name = zend_string_tolower(class_name);
1191		if(!unserialize_allowed_class(lc_name, var_hash)) {
1192			zend_string_release_ex(lc_name, 0);
1193			if (!zend_is_valid_class_name(class_name)) {
1194				zend_string_release_ex(class_name, 0);
1195				return 0;
1196			}
1197			incomplete_class = 1;
1198			ce = PHP_IC_ENTRY;
1199			break;
1200		}
1201
1202		if ((*var_hash)->allowed_classes && ZSTR_HAS_CE_CACHE(class_name)) {
1203			ce = ZSTR_GET_CE_CACHE(class_name);
1204			if (ce) {
1205				zend_string_release_ex(lc_name, 0);
1206				break;
1207			}
1208		}
1209
1210		ce = zend_hash_find_ptr(EG(class_table), lc_name);
1211		if (ce
1212		 && (ce->ce_flags & ZEND_ACC_LINKED)
1213		 && !(ce->ce_flags & ZEND_ACC_ANON_CLASS)) {
1214			zend_string_release_ex(lc_name, 0);
1215			break;
1216		}
1217
1218		if (!ZSTR_HAS_CE_CACHE(class_name) && !zend_is_valid_class_name(class_name)) {
1219			zend_string_release_ex(lc_name, 0);
1220			zend_string_release_ex(class_name, 0);
1221			return 0;
1222		}
1223
1224		/* Try to find class directly */
1225		BG(serialize_lock)++;
1226		ce = zend_lookup_class_ex(class_name, lc_name, 0);
1227		BG(serialize_lock)--;
1228		zend_string_release_ex(lc_name, 0);
1229		if (EG(exception)) {
1230			zend_string_release_ex(class_name, 0);
1231			return 0;
1232		}
1233
1234		if (ce) {
1235			break;
1236		}
1237
1238		/* Check for unserialize callback */
1239		if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) {
1240			incomplete_class = 1;
1241			ce = PHP_IC_ENTRY;
1242			break;
1243		}
1244
1245		/* Call unserialize callback */
1246		ZVAL_STRING(&user_func, PG(unserialize_callback_func));
1247
1248		ZVAL_STR(&args[0], class_name);
1249		BG(serialize_lock)++;
1250		call_user_function(NULL, NULL, &user_func, &retval, 1, args);
1251		BG(serialize_lock)--;
1252		zval_ptr_dtor(&retval);
1253
1254		if (EG(exception)) {
1255			zend_string_release_ex(class_name, 0);
1256			zval_ptr_dtor(&user_func);
1257			return 0;
1258		}
1259
1260		/* The callback function may have defined the class */
1261		BG(serialize_lock)++;
1262		if ((ce = zend_lookup_class(class_name)) == NULL) {
1263			php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", Z_STRVAL(user_func));
1264			incomplete_class = 1;
1265			ce = PHP_IC_ENTRY;
1266		}
1267		BG(serialize_lock)--;
1268
1269		zval_ptr_dtor(&user_func);
1270	} while (0);
1271
1272	*p = YYCURSOR;
1273
1274	if (ce->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) {
1275		zend_throw_exception_ex(NULL, 0, "Unserialization of '%s' is not allowed",
1276			ZSTR_VAL(ce->name));
1277		zend_string_release_ex(class_name, 0);
1278		return 0;
1279	}
1280
1281	if (custom_object) {
1282		int ret;
1283
1284		ret = object_custom(UNSERIALIZE_PASSTHRU, ce);
1285
1286		if (ret && incomplete_class) {
1287			php_store_class_name(rval, class_name);
1288		}
1289		zend_string_release_ex(class_name, 0);
1290		return ret;
1291	}
1292
1293	if (*p >= max - 2) {
1294		zend_error(E_WARNING, "Bad unserialize data");
1295		zend_string_release_ex(class_name, 0);
1296		return 0;
1297	}
1298
1299	elements = parse_iv2(*p + 2, p);
1300	if (elements < 0 || IS_FAKE_ELEM_COUNT(elements, max - YYCURSOR)) {
1301		zend_string_release_ex(class_name, 0);
1302		return 0;
1303	}
1304
1305	YYCURSOR = *p;
1306
1307	if (*(YYCURSOR) != ':') {
1308		return 0;
1309	}
1310	if (*(YYCURSOR+1) != '{') {
1311		*p = YYCURSOR+1;
1312		return 0;
1313	}
1314
1315	*p += 2;
1316
1317	has_unserialize = !incomplete_class && ce->__unserialize;
1318
1319	/* If this class implements Serializable, it should not land here but in object_custom().
1320	 * The passed string obviously doesn't descend from the regular serializer. However, if
1321	 * there is both Serializable::unserialize() and __unserialize(), then both may be used,
1322	 * depending on the serialization format. */
1323	if (ce->serialize != NULL && !has_unserialize) {
1324		zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ZSTR_VAL(ce->name));
1325		zend_string_release_ex(class_name, 0);
1326		return 0;
1327	}
1328
1329	if (object_init_ex(rval, ce) == FAILURE) {
1330		zend_string_release_ex(class_name, 0);
1331		return 0;
1332	}
1333
1334	if (incomplete_class) {
1335		php_store_class_name(rval, class_name);
1336	}
1337	zend_string_release_ex(class_name, 0);
1338
1339	return object_common(UNSERIALIZE_PASSTHRU, elements, has_unserialize);
1340}
1341
1342"E:" uiv ":" ["] {
1343	if (!var_hash) return 0;
1344
1345	size_t len = parse_uiv(start + 2);
1346	size_t maxlen = max - YYCURSOR;
1347	if (maxlen < len || len == 0) {
1348		*p = start + 2;
1349		return 0;
1350	}
1351
1352	char *str = (char *) YYCURSOR;
1353	YYCURSOR += len;
1354
1355	if (*(YYCURSOR) != '"') {
1356		*p = YYCURSOR;
1357		return 0;
1358	}
1359	if (*(YYCURSOR+1) != ';') {
1360		*p = YYCURSOR+1;
1361		return 0;
1362	}
1363
1364	char *colon_ptr = memchr(str, ':', len);
1365	if (colon_ptr == NULL) {
1366		php_error_docref(NULL, E_WARNING, "Invalid enum name '%.*s' (missing colon)", (int) len, str);
1367		return 0;
1368	}
1369	size_t colon_pos = colon_ptr - str;
1370
1371	zend_string *enum_name = zend_string_init(str, colon_pos, 0);
1372	zend_string *case_name = zend_string_init(&str[colon_pos + 1], len - colon_pos - 1, 0);
1373
1374	if (!zend_is_valid_class_name(enum_name)) {
1375		goto fail;
1376	}
1377
1378	zend_class_entry *ce = zend_lookup_class(enum_name);
1379	if (!ce) {
1380		php_error_docref(NULL, E_WARNING, "Class '%s' not found", ZSTR_VAL(enum_name));
1381		goto fail;
1382	}
1383	if (!(ce->ce_flags & ZEND_ACC_ENUM)) {
1384		php_error_docref(NULL, E_WARNING, "Class '%s' is not an enum", ZSTR_VAL(enum_name));
1385		goto fail;
1386	}
1387
1388	YYCURSOR += 2;
1389	*p = YYCURSOR;
1390
1391	zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), case_name);
1392	if (!c) {
1393		php_error_docref(NULL, E_WARNING, "Undefined constant %s::%s", ZSTR_VAL(enum_name), ZSTR_VAL(case_name));
1394		goto fail;
1395	}
1396
1397	if (!(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE)) {
1398		php_error_docref(NULL, E_WARNING, "%s::%s is not an enum case", ZSTR_VAL(enum_name), ZSTR_VAL(case_name));
1399		goto fail;
1400	}
1401
1402	zend_string_release_ex(enum_name, 0);
1403	zend_string_release_ex(case_name, 0);
1404
1405	zval *value = &c->value;
1406	if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
1407		if (zval_update_constant_ex(value, c->ce) == FAILURE) {
1408			return 0;
1409		}
1410	}
1411	ZEND_ASSERT(Z_TYPE_P(value) == IS_OBJECT);
1412	ZVAL_COPY(rval, value);
1413
1414	return 1;
1415
1416fail:
1417	zend_string_release_ex(enum_name, 0);
1418	zend_string_release_ex(case_name, 0);
1419	return 0;
1420}
1421
1422"}" {
1423	/* this is the case where we have less data than planned */
1424	php_error_docref(NULL, E_WARNING, "Unexpected end of serialized data");
1425	return 0; /* not sure if it should be 0 or 1 here? */
1426}
1427
1428any	{ return 0; }
1429
1430*/
1431
1432	return 0;
1433}
1434