PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - lcov_data/ext/standard - var_unserializer.re (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 404 447 90.4 %
Date: 2022-01-16 Functions: 21 21 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Sun, 16 Jan 2022 08:19:26 +0000 (6 days ago)

Copyright © 2005-2022 The PHP Group
All rights reserved.