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