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