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