1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 7 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2018 The PHP Group | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 3.01 of the PHP license, | 8 | that is bundled with this package in the file LICENSE, and is | 9 | available through the world-wide-web at the following url: | 10 | http://www.php.net/license/3_01.txt | 11 | If you did not receive a copy of the PHP license and are unable to | 12 | obtain it through the world-wide-web, please send a note to | 13 | license@php.net so we can mail you a copy immediately. | 14 +----------------------------------------------------------------------+ 15 | Author: Sascha Schumann <sascha@schumann.cx> | 16 +----------------------------------------------------------------------+ 17*/ 18 19#include "php.h" 20#include "ext/standard/php_var.h" 21#include "php_incomplete_class.h" 22#include "zend_portability.h" 23 24struct php_unserialize_data { 25 void *first; 26 void *last; 27 void *first_dtor; 28 void *last_dtor; 29 HashTable *allowed_classes; 30}; 31 32PHPAPI php_unserialize_data_t php_var_unserialize_init() { 33 php_unserialize_data_t d; 34 /* fprintf(stderr, "UNSERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ 35 if (BG(serialize_lock) || !BG(unserialize).level) { 36 d = ecalloc(1, sizeof(struct php_unserialize_data)); 37 if (!BG(serialize_lock)) { 38 BG(unserialize).data = d; 39 BG(unserialize).level = 1; 40 } 41 } else { 42 d = BG(unserialize).data; 43 ++BG(unserialize).level; 44 } 45 return d; 46} 47 48PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) { 49 /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ 50 if (BG(serialize_lock) || BG(unserialize).level == 1) { 51 var_destroy(&d); 52 efree(d); 53 } 54 if (!BG(serialize_lock) && !--BG(unserialize).level) { 55 BG(unserialize).data = NULL; 56 } 57} 58 59PHPAPI HashTable *php_var_unserialize_get_allowed_classes(php_unserialize_data_t d) { 60 return d->allowed_classes; 61} 62PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) { 63 d->allowed_classes = classes; 64} 65 66 67/* {{{ reference-handling for unserializer: var_* */ 68#define VAR_ENTRIES_MAX 1024 69#define VAR_ENTRIES_DBG 0 70 71/* VAR_FLAG used in var_dtor entries to signify an entry on which __wakeup should be called */ 72#define VAR_WAKEUP_FLAG 1 73 74typedef struct { 75 zval *data[VAR_ENTRIES_MAX]; 76 zend_long used_slots; 77 void *next; 78} var_entries; 79 80typedef struct { 81 zval data[VAR_ENTRIES_MAX]; 82 zend_long used_slots; 83 void *next; 84} var_dtor_entries; 85 86static inline void var_push(php_unserialize_data_t *var_hashx, zval *rval) 87{ 88 var_entries *var_hash = (*var_hashx)->last; 89#if VAR_ENTRIES_DBG 90 fprintf(stderr, "var_push(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_P(rval)); 91#endif 92 93 if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) { 94 var_hash = emalloc(sizeof(var_entries)); 95 var_hash->used_slots = 0; 96 var_hash->next = 0; 97 98 if (!(*var_hashx)->first) { 99 (*var_hashx)->first = var_hash; 100 } else { 101 ((var_entries *) (*var_hashx)->last)->next = var_hash; 102 } 103 104 (*var_hashx)->last = var_hash; 105 } 106 107 var_hash->data[var_hash->used_slots++] = rval; 108} 109 110PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval *rval) 111{ 112 zval *tmp_var = var_tmp_var(var_hashx); 113 if (!tmp_var) { 114 return; 115 } 116 ZVAL_COPY(tmp_var, rval); 117} 118 119PHPAPI zval *var_tmp_var(php_unserialize_data_t *var_hashx) 120{ 121 var_dtor_entries *var_hash; 122 123 if (!var_hashx || !*var_hashx) { 124 return NULL; 125 } 126 127 var_hash = (*var_hashx)->last_dtor; 128 if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) { 129 var_hash = emalloc(sizeof(var_dtor_entries)); 130 var_hash->used_slots = 0; 131 var_hash->next = 0; 132 133 if (!(*var_hashx)->first_dtor) { 134 (*var_hashx)->first_dtor = var_hash; 135 } else { 136 ((var_dtor_entries *) (*var_hashx)->last_dtor)->next = var_hash; 137 } 138 139 (*var_hashx)->last_dtor = var_hash; 140 } 141 ZVAL_UNDEF(&var_hash->data[var_hash->used_slots]); 142 Z_EXTRA(var_hash->data[var_hash->used_slots]) = 0; 143 return &var_hash->data[var_hash->used_slots++]; 144} 145 146PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval *nzval) 147{ 148 zend_long i; 149 var_entries *var_hash = (*var_hashx)->first; 150#if VAR_ENTRIES_DBG 151 fprintf(stderr, "var_replace(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_P(nzval)); 152#endif 153 154 while (var_hash) { 155 for (i = 0; i < var_hash->used_slots; i++) { 156 if (var_hash->data[i] == ozval) { 157 var_hash->data[i] = nzval; 158 /* do not break here */ 159 } 160 } 161 var_hash = var_hash->next; 162 } 163} 164 165static zval *var_access(php_unserialize_data_t *var_hashx, zend_long id) 166{ 167 var_entries *var_hash = (*var_hashx)->first; 168#if VAR_ENTRIES_DBG 169 fprintf(stderr, "var_access(%ld): %ld\n", var_hash?var_hash->used_slots:-1L, id); 170#endif 171 172 while (id >= VAR_ENTRIES_MAX && var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) { 173 var_hash = var_hash->next; 174 id -= VAR_ENTRIES_MAX; 175 } 176 177 if (!var_hash) return NULL; 178 179 if (id < 0 || id >= var_hash->used_slots) return NULL; 180 181 return var_hash->data[id]; 182} 183 184PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) 185{ 186 void *next; 187 zend_long i; 188 var_entries *var_hash = (*var_hashx)->first; 189 var_dtor_entries *var_dtor_hash = (*var_hashx)->first_dtor; 190 zend_bool wakeup_failed = 0; 191 zval wakeup_name; 192 ZVAL_UNDEF(&wakeup_name); 193 194#if VAR_ENTRIES_DBG 195 fprintf(stderr, "var_destroy(%ld)\n", var_hash?var_hash->used_slots:-1L); 196#endif 197 198 while (var_hash) { 199 next = var_hash->next; 200 efree_size(var_hash, sizeof(var_entries)); 201 var_hash = next; 202 } 203 204 while (var_dtor_hash) { 205 for (i = 0; i < var_dtor_hash->used_slots; i++) { 206 zval *zv = &var_dtor_hash->data[i]; 207#if VAR_ENTRIES_DBG 208 fprintf(stderr, "var_destroy dtor(%p, %ld)\n", var_dtor_hash->data[i], Z_REFCOUNT_P(var_dtor_hash->data[i])); 209#endif 210 211 /* Perform delayed __wakeup calls */ 212 if (Z_EXTRA_P(zv) == VAR_WAKEUP_FLAG) { 213 if (!wakeup_failed) { 214 zval retval; 215 if (Z_ISUNDEF(wakeup_name)) { 216 ZVAL_STRINGL(&wakeup_name, "__wakeup", sizeof("__wakeup") - 1); 217 } 218 219 BG(serialize_lock)++; 220 if (call_user_function(CG(function_table), zv, &wakeup_name, &retval, 0, 0) == FAILURE || Z_ISUNDEF(retval)) { 221 wakeup_failed = 1; 222 GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED); 223 } 224 BG(serialize_lock)--; 225 226 zval_ptr_dtor(&retval); 227 } else { 228 GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED); 229 } 230 } 231 232 i_zval_ptr_dtor(zv ZEND_FILE_LINE_CC); 233 } 234 next = var_dtor_hash->next; 235 efree_size(var_dtor_hash, sizeof(var_dtor_entries)); 236 var_dtor_hash = next; 237 } 238 239 zval_ptr_dtor_nogc(&wakeup_name); 240} 241 242/* }}} */ 243 244static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t maxlen) 245{ 246 size_t i, j; 247 zend_string *str = zend_string_safe_alloc(1, len, 0, 0); 248 unsigned char *end = *(unsigned char **)p+maxlen; 249 250 if (end < *p) { 251 zend_string_efree(str); 252 return NULL; 253 } 254 255 for (i = 0; i < len; i++) { 256 if (*p >= end) { 257 zend_string_efree(str); 258 return NULL; 259 } 260 if (**p != '\\') { 261 ZSTR_VAL(str)[i] = (char)**p; 262 } else { 263 unsigned char ch = 0; 264 265 for (j = 0; j < 2; j++) { 266 (*p)++; 267 if (**p >= '0' && **p <= '9') { 268 ch = (ch << 4) + (**p -'0'); 269 } else if (**p >= 'a' && **p <= 'f') { 270 ch = (ch << 4) + (**p -'a'+10); 271 } else if (**p >= 'A' && **p <= 'F') { 272 ch = (ch << 4) + (**p -'A'+10); 273 } else { 274 zend_string_efree(str); 275 return NULL; 276 } 277 } 278 ZSTR_VAL(str)[i] = (char)ch; 279 } 280 (*p)++; 281 } 282 ZSTR_VAL(str)[i] = 0; 283 ZSTR_LEN(str) = i; 284 return str; 285} 286 287static inline int unserialize_allowed_class( 288 zend_string *class_name, php_unserialize_data_t *var_hashx) 289{ 290 HashTable *classes = (*var_hashx)->allowed_classes; 291 zend_string *lcname; 292 int res; 293 ALLOCA_FLAG(use_heap) 294 295 if(classes == NULL) { 296 return 1; 297 } 298 if(!zend_hash_num_elements(classes)) { 299 return 0; 300 } 301 302 ZSTR_ALLOCA_ALLOC(lcname, ZSTR_LEN(class_name), use_heap); 303 zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(class_name), ZSTR_LEN(class_name)); 304 res = zend_hash_exists(classes, lcname); 305 ZSTR_ALLOCA_FREE(lcname, use_heap); 306 return res; 307} 308 309#define YYFILL(n) do { } while (0) 310#define YYCTYPE unsigned char 311#define YYCURSOR cursor 312#define YYLIMIT limit 313#define YYMARKER marker 314 315 316/*!re2c 317uiv = [0-9]+; 318iv = [+-]? [0-9]+; 319nv = [+-]? ([0-9]* "." [0-9]+|[0-9]+ "." [0-9]*); 320nvexp = (iv | nv) [eE] iv; 321any = [\000-\377]; 322object = [OC]; 323*/ 324 325 326 327static inline zend_long parse_iv2(const unsigned char *p, const unsigned char **q) 328{ 329 zend_ulong result = 0; 330 zend_ulong neg = 0; 331 const unsigned char *start; 332 333 if (*p == '-') { 334 neg = 1; 335 p++; 336 } else if (UNEXPECTED(*p == '+')) { 337 p++; 338 } 339 340 while (UNEXPECTED(*p == '0')) { 341 p++; 342 } 343 344 start = p; 345 346 while (*p >= '0' && *p <= '9') { 347 result = result * 10 + ((zend_ulong)(*p) - '0'); 348 p++; 349 } 350 351 if (q) { 352 *q = p; 353 } 354 355 /* number too long or overflow */ 356 if (UNEXPECTED(p - start > MAX_LENGTH_OF_LONG - 1) 357 || (SIZEOF_ZEND_LONG == 4 358 && UNEXPECTED(p - start == MAX_LENGTH_OF_LONG - 1) 359 && UNEXPECTED(*start > '2')) 360 || UNEXPECTED(result > ZEND_LONG_MAX + neg)) { 361 php_error_docref(NULL, E_WARNING, "Numerical result out of range"); 362 return (!neg) ? ZEND_LONG_MAX : ZEND_LONG_MIN; 363 } 364 365 return (zend_long) ((!neg) ? result : -result); 366} 367 368static inline zend_long parse_iv(const unsigned char *p) 369{ 370 return parse_iv2(p, NULL); 371} 372 373/* no need to check for length - re2c already did */ 374static inline size_t parse_uiv(const unsigned char *p) 375{ 376 unsigned char cursor; 377 size_t result = 0; 378 379 while (1) { 380 cursor = *p; 381 if (cursor >= '0' && cursor <= '9') { 382 result = result * 10 + (size_t)(cursor - (unsigned char)'0'); 383 } else { 384 break; 385 } 386 p++; 387 } 388 return result; 389} 390 391#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash 392#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash 393 394static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key); 395 396static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) 397{ 398 while (elements-- > 0) { 399 zval key, *data, d, *old_data; 400 zend_ulong idx; 401 402 ZVAL_UNDEF(&key); 403 404 if (!php_var_unserialize_internal(&key, p, max, NULL, 1)) { 405 zval_ptr_dtor(&key); 406 return 0; 407 } 408 409 data = NULL; 410 ZVAL_UNDEF(&d); 411 412 if (!objprops) { 413 if (Z_TYPE(key) == IS_LONG) { 414 idx = Z_LVAL(key); 415numeric_key: 416 if (UNEXPECTED((old_data = zend_hash_index_find(ht, idx)) != NULL)) { 417 //??? update hash 418 var_push_dtor(var_hash, old_data); 419 data = zend_hash_index_update(ht, idx, &d); 420 } else { 421 data = zend_hash_index_add_new(ht, idx, &d); 422 } 423 } else if (Z_TYPE(key) == IS_STRING) { 424 if (UNEXPECTED(ZEND_HANDLE_NUMERIC(Z_STR(key), idx))) { 425 goto numeric_key; 426 } 427 if (UNEXPECTED((old_data = zend_hash_find(ht, Z_STR(key))) != NULL)) { 428 //??? update hash 429 var_push_dtor(var_hash, old_data); 430 data = zend_hash_update(ht, Z_STR(key), &d); 431 } else { 432 data = zend_hash_add_new(ht, Z_STR(key), &d); 433 } 434 } else { 435 zval_ptr_dtor(&key); 436 return 0; 437 } 438 } else { 439 if (EXPECTED(Z_TYPE(key) == IS_STRING)) { 440string_key: 441 if (Z_TYPE_P(rval) == IS_OBJECT 442 && zend_hash_num_elements(&Z_OBJCE_P(rval)->properties_info) > 0) { 443 zend_property_info *existing_propinfo; 444 zend_string *new_key; 445 const char *unmangled_class = NULL; 446 const char *unmangled_prop; 447 size_t unmangled_prop_len; 448 zend_string *unmangled; 449 450 if (UNEXPECTED(zend_unmangle_property_name_ex(Z_STR(key), &unmangled_class, &unmangled_prop, &unmangled_prop_len) == FAILURE)) { 451 zval_ptr_dtor(&key); 452 return 0; 453 } 454 455 unmangled = zend_string_init(unmangled_prop, unmangled_prop_len, 0); 456 457 existing_propinfo = zend_hash_find_ptr(&Z_OBJCE_P(rval)->properties_info, unmangled); 458 if ((unmangled_class == NULL || !strcmp(unmangled_class, "*") || !strcasecmp(unmangled_class, ZSTR_VAL(Z_OBJCE_P(rval)->name))) 459 && (existing_propinfo != NULL) 460 && (existing_propinfo->flags & ZEND_ACC_PPP_MASK)) { 461 if (existing_propinfo->flags & ZEND_ACC_PROTECTED) { 462 new_key = zend_mangle_property_name( 463 "*", 1, ZSTR_VAL(unmangled), ZSTR_LEN(unmangled), 0); 464 zend_string_release_ex(unmangled, 0); 465 } else if (existing_propinfo->flags & ZEND_ACC_PRIVATE) { 466 if (unmangled_class != NULL && strcmp(unmangled_class, "*") != 0) { 467 new_key = zend_mangle_property_name( 468 unmangled_class, strlen(unmangled_class), 469 ZSTR_VAL(unmangled), ZSTR_LEN(unmangled), 470 0); 471 } else { 472 new_key = zend_mangle_property_name( 473 ZSTR_VAL(existing_propinfo->ce->name), ZSTR_LEN(existing_propinfo->ce->name), 474 ZSTR_VAL(unmangled), ZSTR_LEN(unmangled), 475 0); 476 } 477 zend_string_release_ex(unmangled, 0); 478 } else { 479 ZEND_ASSERT(existing_propinfo->flags & ZEND_ACC_PUBLIC); 480 new_key = unmangled; 481 } 482 zval_ptr_dtor_str(&key); 483 ZVAL_STR(&key, new_key); 484 } else { 485 zend_string_release_ex(unmangled, 0); 486 } 487 } 488 489 if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) { 490 if (Z_TYPE_P(old_data) == IS_INDIRECT) { 491 old_data = Z_INDIRECT_P(old_data); 492 } 493 var_push_dtor(var_hash, old_data); 494 data = zend_hash_update_ind(ht, Z_STR(key), &d); 495 } else { 496 data = zend_hash_add_new(ht, Z_STR(key), &d); 497 } 498 } else if (Z_TYPE(key) == IS_LONG) { 499 /* object properties should include no integers */ 500 convert_to_string(&key); 501 goto string_key; 502 } else { 503 zval_ptr_dtor(&key); 504 return 0; 505 } 506 } 507 508 if (!php_var_unserialize_internal(data, p, max, var_hash, 0)) { 509 zval_ptr_dtor(&key); 510 return 0; 511 } 512 513 var_push_dtor(var_hash, data); 514 zval_ptr_dtor_str(&key); 515 516 if (elements && *(*p-1) != ';' && *(*p-1) != '}') { 517 (*p)--; 518 return 0; 519 } 520 } 521 522 return 1; 523} 524 525static inline int finish_nested_data(UNSERIALIZE_PARAMETER) 526{ 527 if (*p >= max || **p != '}') { 528 return 0; 529 } 530 531 (*p)++; 532 return 1; 533} 534 535static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce) 536{ 537 zend_long datalen; 538 539 datalen = parse_iv2((*p) + 2, p); 540 541 (*p) += 2; 542 543 if (datalen < 0 || (max - (*p)) <= datalen) { 544 zend_error(E_WARNING, "Insufficient data for unserializing - " ZEND_LONG_FMT " required, " ZEND_LONG_FMT " present", datalen, (zend_long)(max - (*p))); 545 return 0; 546 } 547 548 /* Check that '}' is present before calling ce->unserialize() to mitigate issues 549 * with unserialize reading past the end of the passed buffer if the string is not 550 * appropriately terminated (usually NUL terminated, but '}' is also sufficient.) */ 551 if ((*p)[datalen] != '}') { 552 return 0; 553 } 554 555 if (ce->unserialize == NULL) { 556 zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name)); 557 object_init_ex(rval, ce); 558 } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) { 559 return 0; 560 } 561 562 (*p) += datalen + 1; /* +1 for '}' */ 563 return 1; 564} 565 566static inline zend_long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) 567{ 568 zend_long elements; 569 570 if( *p >= max - 2) { 571 zend_error(E_WARNING, "Bad unserialize data"); 572 return -1; 573 } 574 575 elements = parse_iv2((*p) + 2, p); 576 577 (*p) += 2; 578 579 if (ce->serialize == NULL) { 580 object_init_ex(rval, ce); 581 } else { 582 /* If this class implements Serializable, it should not land here but in object_custom(). The passed string 583 obviously doesn't descend from the regular serializer. */ 584 zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ZSTR_VAL(ce->name)); 585 return -1; 586 } 587 588 return elements; 589} 590 591#ifdef PHP_WIN32 592# pragma optimize("", off) 593#endif 594static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements) 595{ 596 HashTable *ht; 597 zend_bool has_wakeup; 598 599 if (Z_TYPE_P(rval) != IS_OBJECT) { 600 return 0; 601 } 602 603 has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY 604 && zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1); 605 606 ht = Z_OBJPROP_P(rval); 607 if (elements >= (zend_long)(HT_MAX_SIZE - zend_hash_num_elements(ht))) { 608 return 0; 609 } 610 611 zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, HT_FLAGS(ht) & HASH_FLAG_PACKED); 612 if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) { 613 if (has_wakeup) { 614 ZVAL_DEREF(rval); 615 GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED); 616 } 617 return 0; 618 } 619 620 ZVAL_DEREF(rval); 621 if (has_wakeup) { 622 /* Delay __wakeup call until end of serialization */ 623 zval *wakeup_var = var_tmp_var(var_hash); 624 ZVAL_COPY(wakeup_var, rval); 625 Z_EXTRA_P(wakeup_var) = VAR_WAKEUP_FLAG; 626 } 627 628 return finish_nested_data(UNSERIALIZE_PASSTHRU); 629} 630#ifdef PHP_WIN32 631# pragma optimize("", on) 632#endif 633 634PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) 635{ 636 var_entries *orig_var_entries = (*var_hash)->last; 637 zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0; 638 int result; 639 640 result = php_var_unserialize_internal(UNSERIALIZE_PASSTHRU, 0); 641 642 if (!result) { 643 /* If the unserialization failed, mark all elements that have been added to var_hash 644 * as NULL. This will forbid their use by other unserialize() calls in the same 645 * unserialization context. */ 646 var_entries *e = orig_var_entries; 647 zend_long s = orig_used_slots; 648 while (e) { 649 for (; s < e->used_slots; s++) { 650 e->data[s] = NULL; 651 } 652 653 e = e->next; 654 s = 0; 655 } 656 } 657 658 return result; 659} 660 661static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key) 662{ 663 const unsigned char *cursor, *limit, *marker, *start; 664 zval *rval_ref; 665 666 limit = max; 667 cursor = *p; 668 669 if (YYCURSOR >= YYLIMIT) { 670 return 0; 671 } 672 673 if (var_hash && (*p)[0] != 'R') { 674 var_push(var_hash, rval); 675 } 676 677 start = cursor; 678 679/*!re2c 680 681"R:" uiv ";" { 682 zend_long id; 683 684 *p = YYCURSOR; 685 if (!var_hash) return 0; 686 687 id = parse_uiv(start + 2) - 1; 688 if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) { 689 return 0; 690 } 691 692 if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) { 693 return 0; 694 } 695 696 if (Z_ISREF_P(rval_ref)) { 697 ZVAL_COPY(rval, rval_ref); 698 } else { 699 ZVAL_NEW_REF(rval_ref, rval_ref); 700 ZVAL_COPY(rval, rval_ref); 701 } 702 703 return 1; 704} 705 706"r:" uiv ";" { 707 zend_long id; 708 709 *p = YYCURSOR; 710 if (!var_hash) return 0; 711 712 id = parse_uiv(start + 2) - 1; 713 if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) { 714 return 0; 715 } 716 717 if (rval_ref == rval) { 718 return 0; 719 } 720 721 ZVAL_DEREF(rval_ref); 722 if (Z_TYPE_P(rval_ref) != IS_OBJECT) { 723 return 0; 724 } 725 726 ZVAL_COPY(rval, rval_ref); 727 728 return 1; 729} 730 731"N;" { 732 *p = YYCURSOR; 733 ZVAL_NULL(rval); 734 return 1; 735} 736 737"b:0;" { 738 *p = YYCURSOR; 739 ZVAL_FALSE(rval); 740 return 1; 741} 742 743"b:1;" { 744 *p = YYCURSOR; 745 ZVAL_TRUE(rval); 746 return 1; 747} 748 749"i:" iv ";" { 750#if SIZEOF_ZEND_LONG == 4 751 int digits = YYCURSOR - start - 3; 752 753 if (start[2] == '-' || start[2] == '+') { 754 digits--; 755 } 756 757 /* Use double for large zend_long values that were serialized on a 64-bit system */ 758 if (digits >= MAX_LENGTH_OF_LONG - 1) { 759 if (digits == MAX_LENGTH_OF_LONG - 1) { 760 int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1); 761 762 if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) { 763 goto use_double; 764 } 765 } else { 766 goto use_double; 767 } 768 } 769#endif 770 *p = YYCURSOR; 771 ZVAL_LONG(rval, parse_iv(start + 2)); 772 return 1; 773} 774 775"d:" ("NAN" | "-"? "INF") ";" { 776 *p = YYCURSOR; 777 778 if (!strncmp((char*)start + 2, "NAN", 3)) { 779 ZVAL_DOUBLE(rval, ZEND_NAN); 780 } else if (!strncmp((char*)start + 2, "INF", 3)) { 781 ZVAL_DOUBLE(rval, ZEND_INFINITY); 782 } else if (!strncmp((char*)start + 2, "-INF", 4)) { 783 ZVAL_DOUBLE(rval, -ZEND_INFINITY); 784 } else { 785 ZVAL_NULL(rval); 786 } 787 788 return 1; 789} 790 791"d:" (iv | nv | nvexp) ";" { 792#if SIZEOF_ZEND_LONG == 4 793use_double: 794#endif 795 *p = YYCURSOR; 796 ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL)); 797 return 1; 798} 799 800"s:" uiv ":" ["] { 801 size_t len, maxlen; 802 char *str; 803 804 len = parse_uiv(start + 2); 805 maxlen = max - YYCURSOR; 806 if (maxlen < len) { 807 *p = start + 2; 808 return 0; 809 } 810 811 str = (char*)YYCURSOR; 812 813 YYCURSOR += len; 814 815 if (*(YYCURSOR) != '"') { 816 *p = YYCURSOR; 817 return 0; 818 } 819 820 if (*(YYCURSOR + 1) != ';') { 821 *p = YYCURSOR + 1; 822 return 0; 823 } 824 825 YYCURSOR += 2; 826 *p = YYCURSOR; 827 828 if (len == 0) { 829 ZVAL_EMPTY_STRING(rval); 830 } else if (len == 1) { 831 ZVAL_INTERNED_STR(rval, ZSTR_CHAR((zend_uchar)*str)); 832 } else if (as_key) { 833 ZVAL_STR(rval, zend_string_init_interned(str, len, 0)); 834 } else { 835 ZVAL_STRINGL(rval, str, len); 836 } 837 return 1; 838} 839 840"S:" uiv ":" ["] { 841 size_t len, maxlen; 842 zend_string *str; 843 844 len = parse_uiv(start + 2); 845 maxlen = max - YYCURSOR; 846 if (maxlen < len) { 847 *p = start + 2; 848 return 0; 849 } 850 851 if ((str = unserialize_str(&YYCURSOR, len, maxlen)) == NULL) { 852 return 0; 853 } 854 855 if (*(YYCURSOR) != '"') { 856 zend_string_efree(str); 857 *p = YYCURSOR; 858 return 0; 859 } 860 861 if (*(YYCURSOR + 1) != ';') { 862 efree(str); 863 *p = YYCURSOR + 1; 864 return 0; 865 } 866 867 YYCURSOR += 2; 868 *p = YYCURSOR; 869 870 ZVAL_STR(rval, str); 871 return 1; 872} 873 874"a:" uiv ":" "{" { 875 zend_long elements = parse_iv(start + 2); 876 /* use iv() not uiv() in order to check data range */ 877 *p = YYCURSOR; 878 if (!var_hash) return 0; 879 880 if (elements < 0 || elements >= HT_MAX_SIZE) { 881 return 0; 882 } 883 884 if (elements) { 885 array_init_size(rval, elements); 886 /* we can't convert from packed to hash during unserialization, because 887 reference to some zvals might be keept in var_hash (to support references) */ 888 zend_hash_real_init_mixed(Z_ARRVAL_P(rval)); 889 } else { 890 ZVAL_EMPTY_ARRAY(rval); 891 return finish_nested_data(UNSERIALIZE_PASSTHRU); 892 } 893 894 /* The array may contain references to itself, in which case we'll be modifying an 895 * rc>1 array. This is okay, since the array is, ostensibly, only visible to 896 * unserialize (in practice unserialization handlers also see it). Ideally we should 897 * prohibit "r:" references to non-objects, as we only generate them for objects. */ 898 HT_ALLOW_COW_VIOLATION(Z_ARRVAL_P(rval)); 899 900 if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) { 901 return 0; 902 } 903 904 return finish_nested_data(UNSERIALIZE_PASSTHRU); 905} 906 907"o:" uiv ":" ["] { 908 zend_long elements; 909 if (!var_hash) return 0; 910 911 elements = object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR); 912 if (elements < 0 || elements >= HT_MAX_SIZE) { 913 return 0; 914 } 915 return object_common2(UNSERIALIZE_PASSTHRU, elements); 916} 917 918object ":" uiv ":" ["] { 919 size_t len, len2, len3, maxlen; 920 zend_long elements; 921 char *str; 922 zend_string *class_name; 923 zend_class_entry *ce; 924 int incomplete_class = 0; 925 926 int custom_object = 0; 927 928 zval user_func; 929 zval retval; 930 zval args[1]; 931 932 if (!var_hash) return 0; 933 if (*start == 'C') { 934 custom_object = 1; 935 } 936 937 len2 = len = parse_uiv(start + 2); 938 maxlen = max - YYCURSOR; 939 if (maxlen < len || len == 0) { 940 *p = start + 2; 941 return 0; 942 } 943 944 str = (char*)YYCURSOR; 945 946 YYCURSOR += len; 947 948 if (*(YYCURSOR) != '"') { 949 *p = YYCURSOR; 950 return 0; 951 } 952 if (*(YYCURSOR+1) != ':') { 953 *p = YYCURSOR+1; 954 return 0; 955 } 956 957 len3 = strspn(str, "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\"); 958 if (len3 != len) 959 { 960 *p = YYCURSOR + len3 - len; 961 return 0; 962 } 963 964 class_name = zend_string_init(str, len, 0); 965 966 do { 967 if(!unserialize_allowed_class(class_name, var_hash)) { 968 incomplete_class = 1; 969 ce = PHP_IC_ENTRY; 970 break; 971 } 972 973 /* Try to find class directly */ 974 BG(serialize_lock)++; 975 ce = zend_lookup_class(class_name); 976 if (ce) { 977 BG(serialize_lock)--; 978 if (EG(exception)) { 979 zend_string_release_ex(class_name, 0); 980 return 0; 981 } 982 break; 983 } 984 BG(serialize_lock)--; 985 986 if (EG(exception)) { 987 zend_string_release_ex(class_name, 0); 988 return 0; 989 } 990 991 /* Check for unserialize callback */ 992 if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) { 993 incomplete_class = 1; 994 ce = PHP_IC_ENTRY; 995 break; 996 } 997 998 /* Call unserialize callback */ 999 ZVAL_STRING(&user_func, PG(unserialize_callback_func)); 1000 1001 ZVAL_STR_COPY(&args[0], class_name); 1002 BG(serialize_lock)++; 1003 if (call_user_function_ex(CG(function_table), NULL, &user_func, &retval, 1, args, 0, NULL) != SUCCESS) { 1004 BG(serialize_lock)--; 1005 if (EG(exception)) { 1006 zend_string_release_ex(class_name, 0); 1007 zval_ptr_dtor(&user_func); 1008 zval_ptr_dtor(&args[0]); 1009 return 0; 1010 } 1011 php_error_docref(NULL, E_WARNING, "defined (%s) but not found", Z_STRVAL(user_func)); 1012 incomplete_class = 1; 1013 ce = PHP_IC_ENTRY; 1014 zval_ptr_dtor(&user_func); 1015 zval_ptr_dtor(&args[0]); 1016 break; 1017 } 1018 BG(serialize_lock)--; 1019 zval_ptr_dtor(&retval); 1020 if (EG(exception)) { 1021 zend_string_release_ex(class_name, 0); 1022 zval_ptr_dtor(&user_func); 1023 zval_ptr_dtor(&args[0]); 1024 return 0; 1025 } 1026 1027 /* The callback function may have defined the class */ 1028 BG(serialize_lock)++; 1029 if ((ce = zend_lookup_class(class_name)) == NULL) { 1030 php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", Z_STRVAL(user_func)); 1031 incomplete_class = 1; 1032 ce = PHP_IC_ENTRY; 1033 } 1034 BG(serialize_lock)--; 1035 1036 zval_ptr_dtor(&user_func); 1037 zval_ptr_dtor(&args[0]); 1038 break; 1039 } while (1); 1040 1041 *p = YYCURSOR; 1042 1043 if (custom_object) { 1044 int ret; 1045 1046 ret = object_custom(UNSERIALIZE_PASSTHRU, ce); 1047 1048 if (ret && incomplete_class) { 1049 php_store_class_name(rval, ZSTR_VAL(class_name), len2); 1050 } 1051 zend_string_release_ex(class_name, 0); 1052 return ret; 1053 } 1054 1055 elements = object_common1(UNSERIALIZE_PASSTHRU, ce); 1056 1057 if (elements < 0) { 1058 zend_string_release_ex(class_name, 0); 1059 return 0; 1060 } 1061 1062 if (incomplete_class) { 1063 php_store_class_name(rval, ZSTR_VAL(class_name), len2); 1064 } 1065 zend_string_release_ex(class_name, 0); 1066 1067 return object_common2(UNSERIALIZE_PASSTHRU, elements); 1068} 1069 1070"}" { 1071 /* this is the case where we have less data than planned */ 1072 php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data"); 1073 return 0; /* not sure if it should be 0 or 1 here? */ 1074} 1075 1076any { return 0; } 1077 1078*/ 1079 1080 return 0; 1081} 1082