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