Coverage Report

Created: 2026-10-28 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/standard/var_unserializer.c
Line
Count
Source
1
/* Generated by re2c 3.1 */
2
#line 1 "ext/standard/var_unserializer.re"
3
/*
4
  +----------------------------------------------------------------------+
5
  | Copyright © The PHP Group and Contributors.                          |
6
  +----------------------------------------------------------------------+
7
  | This source file is subject to the Modified BSD License that is      |
8
  | bundled with this package in the file LICENSE, and is available      |
9
  | through the World Wide Web at <https://www.php.net/license/>.        |
10
  |                                                                      |
11
  | SPDX-License-Identifier: BSD-3-Clause                                |
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
#include "zend_exceptions.h"
22
#include "zend_objects.h"
23
24
/* {{{ reference-handling for unserializer: var_* */
25
5.18M
#define VAR_ENTRIES_MAX 1018     /* 1024 - offsetof(php_unserialize_data, entries) / sizeof(void*) */
26
785k
#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
1.36M
#define VAR_WAKEUP_FLAG 1
32
1.71M
#define VAR_UNSERIALIZE_FLAG 2
33
34
/* Each element is encoded using at least 2 characters. */
35
#define IS_FAKE_ELEM_COUNT(num_elems, serialized_len) \
36
1.26M
  ((num_elems) > (serialized_len) / 2)
37
38
typedef struct {
39
  zend_long used_slots;
40
  void *next;
41
  zval *data[VAR_ENTRIES_MAX];
42
} var_entries;
43
44
typedef struct {
45
  zend_long used_slots;
46
  void *next;
47
  zval data[VAR_DTOR_ENTRIES_MAX];
48
} var_dtor_entries;
49
50
struct php_unserialize_data {
51
  var_entries      *last;
52
  var_dtor_entries *first_dtor;
53
  var_dtor_entries *last_dtor;
54
  HashTable        *allowed_classes;
55
  HashTable        *ref_props;
56
  zend_long         cur_depth;
57
  zend_long         max_depth;
58
  /* Represent every object as __PHP_Incomplete_Class, without resolving its
59
   * class: no autoloading, no unserialize_callback_func, no __wakeup() or
60
   * __unserialize(). O:, C: and E: values all serialize back to the same
61
   * bytes. */
62
  bool              incomplete_classes_only;
63
  var_entries       entries;
64
};
65
66
130k
PHPAPI php_unserialize_data_t php_var_unserialize_init(void) {
67
130k
  php_unserialize_data_t d;
68
  /* fprintf(stderr, "UNSERIALIZE_INIT    == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */
69
130k
  if (BG(serialize_lock) || !BG(unserialize).level) {
70
129k
    d = emalloc(sizeof(struct php_unserialize_data));
71
129k
    d->last = &d->entries;
72
129k
    d->first_dtor = d->last_dtor = NULL;
73
129k
    d->allowed_classes = NULL;
74
129k
    d->ref_props = NULL;
75
129k
    d->cur_depth = 0;
76
129k
    d->max_depth = BG(unserialize_max_depth);
77
129k
    d->incomplete_classes_only = false;
78
129k
    d->entries.used_slots = 0;
79
129k
    d->entries.next = NULL;
80
129k
    if (!BG(serialize_lock)) {
81
129k
      BG(unserialize).data = d;
82
129k
      BG(unserialize).level = 1;
83
129k
    }
84
129k
  } else {
85
220
    d = BG(unserialize).data;
86
220
    ++BG(unserialize).level;
87
220
  }
88
130k
  return d;
89
130k
}
90
91
130k
PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) {
92
  /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */
93
130k
  if (BG(serialize_lock)) {
94
0
    var_destroy(&d);
95
0
    efree(d);
96
130k
  } else if (!--BG(unserialize).level) {
97
129k
    BG(unserialize).data = NULL;
98
129k
    var_destroy(&d);
99
129k
    efree(d);
100
129k
  }
101
130k
}
102
103
123
PHPAPI HashTable *php_var_unserialize_get_allowed_classes(php_unserialize_data_t d) {
104
123
  return d->allowed_classes;
105
123
}
106
123
PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) {
107
123
  d->allowed_classes = classes;
108
123
}
109
110
0
PHPAPI void php_var_unserialize_set_incomplete_classes_only(php_unserialize_data_t d, bool incomplete_classes_only) {
111
0
  d->incomplete_classes_only = incomplete_classes_only;
112
0
}
113
114
123
PHPAPI void php_var_unserialize_set_max_depth(php_unserialize_data_t d, zend_long max_depth) {
115
123
  d->max_depth = max_depth;
116
123
}
117
123
PHPAPI zend_long php_var_unserialize_get_max_depth(php_unserialize_data_t d) {
118
123
  return d->max_depth;
119
123
}
120
121
123
PHPAPI void php_var_unserialize_set_cur_depth(php_unserialize_data_t d, zend_long cur_depth) {
122
123
  d->cur_depth = cur_depth;
123
123
}
124
123
PHPAPI zend_long php_var_unserialize_get_cur_depth(php_unserialize_data_t d) {
125
123
  return d->cur_depth;
126
123
}
127
128
static inline void var_push(php_unserialize_data_t *var_hashx, zval *rval)
129
3.34M
{
130
3.34M
  var_entries *var_hash = (*var_hashx)->last;
131
#if VAR_ENTRIES_DBG
132
  fprintf(stderr, "var_push(" ZEND_LONG_FMT "): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_P(rval));
133
#endif
134
135
3.34M
  if (var_hash->used_slots == VAR_ENTRIES_MAX) {
136
2.34k
    var_hash = emalloc(sizeof(var_entries));
137
2.34k
    var_hash->used_slots = 0;
138
2.34k
    var_hash->next = 0;
139
140
2.34k
    (*var_hashx)->last->next = var_hash;
141
2.34k
    (*var_hashx)->last = var_hash;
142
2.34k
  }
143
144
3.34M
  var_hash->data[var_hash->used_slots++] = rval;
145
3.34M
}
146
147
PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval *rval)
148
109
{
149
109
  if (Z_REFCOUNTED_P(rval)) {
150
46
    zval *tmp_var = var_tmp_var(var_hashx);
151
46
    if (!tmp_var) {
152
0
      return;
153
0
    }
154
46
    ZVAL_COPY(tmp_var, rval);
155
46
  }
156
109
}
157
158
static zend_never_inline void var_push_dtor_value(php_unserialize_data_t *var_hashx, zval *rval)
159
893k
{
160
893k
  if (Z_REFCOUNTED_P(rval)) {
161
444k
    zval *tmp_var = var_tmp_var(var_hashx);
162
444k
    if (!tmp_var) {
163
0
      return;
164
0
    }
165
444k
    ZVAL_COPY_VALUE(tmp_var, rval);
166
444k
  }
167
893k
}
168
169
static zend_always_inline void var_restore_prop_default(php_unserialize_data_t *var_hash, zend_object *obj, zend_property_info *info, zval *data)
170
14.7k
{
171
  /* A partially/incorrectly unserialized value may violate the property's
172
   * declared type, so restore the default and keep the slot consistent. */
173
14.7k
  zval *tmp = &obj->ce->default_properties_table[OBJ_PROP_TO_NUM(info->offset)];
174
14.7k
  if (Z_REFCOUNTED_P(data)) {
175
14.5k
    var_push_dtor_value(var_hash, data);
176
14.5k
  }
177
14.7k
  ZVAL_COPY_OR_DUP_PROP(data, tmp);
178
14.7k
}
179
180
static zend_always_inline zval *tmp_var(php_unserialize_data_t *var_hashx, zend_long num)
181
903k
{
182
903k
    var_dtor_entries *var_hash;
183
903k
  zend_long used_slots;
184
185
903k
    if (!var_hashx || !*var_hashx || num < 1) {
186
0
        return NULL;
187
0
    }
188
189
903k
    var_hash = (*var_hashx)->last_dtor;
190
903k
    if (!var_hash || var_hash->used_slots + num > VAR_DTOR_ENTRIES_MAX) {
191
121k
        var_hash = emalloc(sizeof(var_dtor_entries));
192
121k
        var_hash->used_slots = 0;
193
121k
        var_hash->next = 0;
194
195
121k
        if (!(*var_hashx)->first_dtor) {
196
117k
            (*var_hashx)->first_dtor = var_hash;
197
117k
        } else {
198
3.70k
            (*var_hashx)->last_dtor->next = var_hash;
199
3.70k
        }
200
201
121k
        (*var_hashx)->last_dtor = var_hash;
202
121k
    }
203
2.23M
  for (used_slots = var_hash->used_slots; var_hash->used_slots < used_slots + num; var_hash->used_slots++) {
204
1.32M
    ZVAL_UNDEF(&var_hash->data[var_hash->used_slots]);
205
1.32M
    Z_EXTRA(var_hash->data[var_hash->used_slots]) = 0;
206
1.32M
  }
207
903k
    return &var_hash->data[used_slots];
208
903k
}
209
210
PHPAPI zval *var_tmp_var(php_unserialize_data_t *var_hashx)
211
479k
{
212
479k
    return tmp_var(var_hashx, 1);
213
479k
}
214
215
PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval *nzval)
216
115
{
217
115
  zend_long i;
218
115
  var_entries *var_hash = &(*var_hashx)->entries;
219
#if VAR_ENTRIES_DBG
220
  fprintf(stderr, "var_replace(" ZEND_LONG_FMT "): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_P(nzval));
221
#endif
222
223
273
  while (var_hash) {
224
47.4k
    for (i = 0; i < var_hash->used_slots; i++) {
225
47.2k
      if (var_hash->data[i] == ozval) {
226
16
        var_hash->data[i] = nzval;
227
        /* do not break here */
228
16
      }
229
47.2k
    }
230
158
    var_hash = var_hash->next;
231
158
  }
232
115
}
233
234
static zval *var_access(php_unserialize_data_t *var_hashx, zend_long id)
235
918k
{
236
918k
  var_entries *var_hash = &(*var_hashx)->entries;
237
#if VAR_ENTRIES_DBG
238
  fprintf(stderr, "var_access(" ZEND_LONG_FMT "): " ZEND_LONG_FMT "\n", var_hash?var_hash->used_slots:-1L, id);
239
#endif
240
241
918k
  while (id >= VAR_ENTRIES_MAX && var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) {
242
197
    var_hash = var_hash->next;
243
197
    id -= VAR_ENTRIES_MAX;
244
197
  }
245
246
918k
  if (!var_hash) return NULL;
247
248
918k
  if (id < 0 || id >= var_hash->used_slots) return NULL;
249
250
917k
  return var_hash->data[id];
251
918k
}
252
253
PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
254
129k
{
255
129k
  void *next;
256
129k
  zend_long i;
257
129k
  var_entries *var_hash = (*var_hashx)->entries.next;
258
129k
  var_dtor_entries *var_dtor_hash = (*var_hashx)->first_dtor;
259
129k
  bool delayed_call_failed = 0;
260
261
#if VAR_ENTRIES_DBG
262
  fprintf(stderr, "var_destroy( " ZEND_LONG_FMT ")\n", var_hash?var_hash->used_slots:-1L);
263
#endif
264
265
132k
  while (var_hash) {
266
2.34k
    next = var_hash->next;
267
2.34k
    efree_size(var_hash, sizeof(var_entries));
268
2.34k
    var_hash = next;
269
2.34k
  }
270
271
251k
  while (var_dtor_hash) {
272
1.44M
    for (i = 0; i < var_dtor_hash->used_slots; i++) {
273
1.32M
      zval *zv = &var_dtor_hash->data[i];
274
#if VAR_ENTRIES_DBG
275
      fprintf(stderr, "var_destroy dtor(%p, %ld)\n", &var_dtor_hash->data[i], Z_REFCOUNT_P(&var_dtor_hash->data[i]));
276
#endif
277
278
1.32M
      if (Z_EXTRA_P(zv) == VAR_WAKEUP_FLAG) {
279
        /* Perform delayed __wakeup calls */
280
35.2k
        if (!delayed_call_failed) {
281
21.9k
          ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT);
282
283
21.9k
          zend_function *fn = zend_hash_find_ptr(&Z_OBJCE_P(zv)->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP));
284
21.9k
          zval retval;
285
286
21.9k
          BG(serialize_lock)++;
287
21.9k
          zend_call_known_instance_method(fn, Z_OBJ_P(zv), &retval, 0, NULL);
288
          /* Exception thrown */
289
21.9k
          if (Z_ISUNDEF(retval) || ZEND_HAS_OOM()) {
290
5
            delayed_call_failed = 1;
291
5
            GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
292
5
          }
293
21.9k
          BG(serialize_lock)--;
294
295
21.9k
          zval_ptr_dtor(&retval);
296
21.9k
        } else {
297
13.3k
          GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
298
13.3k
        }
299
1.29M
      } else if (Z_EXTRA_P(zv) == VAR_UNSERIALIZE_FLAG) {
300
        /* Perform delayed __unserialize calls */
301
423k
        if (!delayed_call_failed) {
302
391k
          zval param;
303
391k
          ZVAL_COPY(&param, &var_dtor_hash->data[i + 1]);
304
305
391k
          zend_object_set_properties_reinitable(Z_OBJ_P(zv), /* reinitable */ true);
306
391k
          BG(serialize_lock)++;
307
391k
          zend_call_known_instance_method_with_1_params(
308
391k
            Z_OBJCE_P(zv)->__unserialize, Z_OBJ_P(zv), NULL, &param);
309
391k
          if (EG(exception) || ZEND_HAS_OOM()) {
310
109k
            delayed_call_failed = 1;
311
109k
            GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
312
109k
          }
313
391k
          BG(serialize_lock)--;
314
391k
          zend_object_set_properties_reinitable(Z_OBJ_P(zv), /* reinitable */ false);
315
391k
          zval_ptr_dtor(&param);
316
391k
        } else {
317
31.6k
          GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
318
31.6k
        }
319
423k
      }
320
321
1.32M
      i_zval_ptr_dtor(zv);
322
1.32M
    }
323
121k
    next = var_dtor_hash->next;
324
121k
    efree_size(var_dtor_hash, sizeof(var_dtor_entries));
325
121k
    var_dtor_hash = next;
326
121k
  }
327
328
129k
  if ((*var_hashx)->ref_props) {
329
424
    zend_hash_destroy((*var_hashx)->ref_props);
330
424
    FREE_HASHTABLE((*var_hashx)->ref_props);
331
424
  }
332
129k
}
333
334
/* }}} */
335
336
static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t maxlen)
337
427k
{
338
427k
  size_t i, j;
339
427k
  zend_string *str = zend_string_falloc(len, 0, /* fallible */ true);
340
427k
  if (UNEXPECTED(!str)) {
341
0
    return NULL;
342
0
  }
343
427k
  unsigned char *end = *(unsigned char **)p+maxlen;
344
345
83.1M
  for (i = 0; i < len; i++) {
346
82.7M
    if (*p >= end) {
347
31
      zend_string_efree(str);
348
31
      return NULL;
349
31
    }
350
82.7M
    if (**p != '\\') {
351
82.7M
      ZSTR_VAL(str)[i] = (char)**p;
352
82.7M
    } else {
353
2.94k
      unsigned char ch = 0;
354
355
8.75k
      for (j = 0; j < 2; j++) {
356
5.86k
        (*p)++;
357
5.86k
        if (**p >= '0' && **p <= '9') {
358
2.57k
          ch = (ch << 4) + (**p -'0');
359
3.28k
        } else if (**p >= 'a' && **p <= 'f') {
360
2.27k
          ch = (ch << 4) + (**p -'a'+10);
361
2.27k
        } else if (**p >= 'A' && **p <= 'F') {
362
954
          ch = (ch << 4) + (**p -'A'+10);
363
954
        } else {
364
53
          zend_string_efree(str);
365
53
          return NULL;
366
53
        }
367
5.86k
      }
368
2.89k
      ZSTR_VAL(str)[i] = (char)ch;
369
2.89k
    }
370
82.7M
    (*p)++;
371
82.7M
  }
372
427k
  ZSTR_VAL(str)[i] = 0;
373
427k
  ZSTR_LEN(str) = i;
374
427k
  return str;
375
427k
}
376
377
static inline int unserialize_allowed_class(
378
    zend_string *lcname, php_unserialize_data_t *var_hashx)
379
997k
{
380
997k
  HashTable *classes = (*var_hashx)->allowed_classes;
381
382
997k
  if(classes == NULL) {
383
997k
    return 1;
384
997k
  }
385
0
  if(!zend_hash_num_elements(classes)) {
386
0
    return 0;
387
0
  }
388
389
0
  return zend_hash_exists(classes, lcname);
390
0
}
391
392
11.2k
#define YYFILL(n) do { } while (0)
393
7.56M
#define YYCTYPE unsigned char
394
286M
#define YYCURSOR cursor
395
87.2M
#define YYLIMIT limit
396
7.52M
#define YYMARKER marker
397
398
399
#line 404 "ext/standard/var_unserializer.re"
400
401
402
403
404
static inline zend_long parse_iv2(const unsigned char *p, const unsigned char **q)
405
4.58M
{
406
4.58M
  zend_ulong result = 0;
407
4.58M
  zend_ulong neg = 0;
408
4.58M
  const unsigned char *start;
409
410
4.58M
  if (*p == '-') {
411
102k
    neg = 1;
412
102k
    p++;
413
4.47M
  } else if (UNEXPECTED(*p == '+')) {
414
3.45k
    p++;
415
3.45k
  }
416
417
7.48M
  while (UNEXPECTED(*p == '0')) {
418
2.90M
    p++;
419
2.90M
  }
420
421
4.58M
  start = p;
422
423
13.6M
  while (*p >= '0' && *p <= '9') {
424
9.11M
    result = result * 10 + ((zend_ulong)(*p) - '0');
425
9.11M
    p++;
426
9.11M
  }
427
428
4.58M
  if (q) {
429
996k
    *q = p;
430
996k
  }
431
432
  /* number too long or overflow */
433
4.58M
  if (UNEXPECTED(p - start > MAX_LENGTH_OF_LONG - 1)
434
4.53M
   || (SIZEOF_ZEND_LONG == 4
435
0
    && UNEXPECTED(p - start == MAX_LENGTH_OF_LONG - 1)
436
0
    && UNEXPECTED(*start > '2'))
437
4.53M
   || UNEXPECTED(result > ZEND_LONG_MAX + neg)) {
438
45.7k
    php_error_docref(NULL, E_WARNING, "Numerical result out of range");
439
45.7k
    return (!neg) ? ZEND_LONG_MAX : ZEND_LONG_MIN;
440
45.7k
  }
441
442
4.53M
  return (zend_long) ((!neg) ? result : -result);
443
4.58M
}
444
445
static inline zend_long parse_iv(const unsigned char *p)
446
3.58M
{
447
3.58M
  return parse_iv2(p, NULL);
448
3.58M
}
449
450
/* no need to check for length - re2c already did */
451
static inline size_t parse_uiv(const unsigned char *p)
452
3.04M
{
453
3.04M
  unsigned char cursor;
454
3.04M
  size_t result = 0;
455
456
7.47M
  while (1) {
457
7.47M
    cursor = *p;
458
7.47M
    if (cursor >= '0' && cursor <= '9') {
459
4.42M
      result = result * 10 + (size_t)(cursor - (unsigned char)'0');
460
4.42M
    } else {
461
3.04M
      break;
462
3.04M
    }
463
4.42M
    p++;
464
4.42M
  }
465
3.04M
  return result;
466
3.04M
}
467
468
#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash
469
2.93M
#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash
470
471
static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER);
472
473
static zend_always_inline int process_nested_array_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements)
474
909k
{
475
909k
  if (var_hash) {
476
909k
    if ((*var_hash)->max_depth > 0 && (*var_hash)->cur_depth >= (*var_hash)->max_depth) {
477
9
      php_error_docref(NULL, E_WARNING,
478
9
        "Maximum depth of " ZEND_LONG_FMT " exceeded. "
479
9
        "The depth limit can be changed using the max_depth unserialize() option "
480
9
        "or the unserialize_max_depth ini setting",
481
9
        (*var_hash)->max_depth);
482
9
      return 0;
483
9
    }
484
909k
    (*var_hash)->cur_depth++;
485
909k
  }
486
487
2.61M
  while (elements-- > 0) {
488
2.13M
    zval key, *data;
489
2.13M
    zend_ulong idx;
490
491
2.13M
    ZVAL_UNDEF(&key);
492
493
2.13M
    if (!php_var_unserialize_internal(&key, p, max, NULL)) {
494
1.63k
      zval_ptr_dtor(&key);
495
1.63k
      goto failure;
496
1.63k
    }
497
498
2.13M
    if (Z_TYPE(key) == IS_LONG) {
499
1.70M
      idx = Z_LVAL(key);
500
1.71M
numeric_key:
501
1.71M
      data = zend_hash_index_lookup(ht, idx);
502
1.71M
      if (UNEXPECTED(Z_TYPE_INFO_P(data) != IS_NULL)) {
503
372k
        var_push_dtor_value(var_hash, data);
504
372k
        ZVAL_NULL(data);
505
372k
      }
506
1.71M
    } else if (Z_TYPE(key) == IS_STRING) {
507
420k
      if (UNEXPECTED(ZEND_HANDLE_NUMERIC(Z_STR(key), idx))) {
508
8.68k
        zval_ptr_dtor_str(&key);
509
8.68k
        goto numeric_key;
510
8.68k
      }
511
411k
      data = zend_hash_lookup(ht, Z_STR(key));
512
411k
      if (UNEXPECTED(Z_TYPE_INFO_P(data) != IS_NULL)) {
513
3.06k
        var_push_dtor_value(var_hash, data);
514
3.06k
        ZVAL_NULL(data);
515
3.06k
      }
516
411k
      zval_ptr_dtor_str(&key);
517
411k
    } else {
518
543
      zval_ptr_dtor(&key);
519
543
      goto failure;
520
543
    }
521
522
2.12M
    if (!php_var_unserialize_internal(data, p, max, var_hash)) {
523
419k
      goto failure;
524
419k
    }
525
526
1.71M
    if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
527
0
      (*p)--;
528
0
      goto failure;
529
0
    }
530
531
1.71M
    if (ZEND_HAS_OOM()) {
532
0
      goto failure;
533
0
    }
534
1.71M
  }
535
536
487k
  if (var_hash) {
537
487k
    (*var_hash)->cur_depth--;
538
487k
  }
539
487k
  return 1;
540
541
421k
failure:
542
421k
  if (var_hash) {
543
421k
    (*var_hash)->cur_depth--;
544
421k
  }
545
421k
  return 0;
546
909k
}
547
548
static int is_property_visibility_changed(zend_class_entry *ce, zval *key)
549
1.58M
{
550
1.58M
  if (zend_hash_num_elements(&ce->properties_info) > 0) {
551
950k
    zend_property_info *existing_propinfo = NULL;
552
950k
    const char *unmangled_class = NULL;
553
950k
    const char *unmangled_prop;
554
950k
    size_t unmangled_prop_len;
555
556
950k
    if (UNEXPECTED(zend_unmangle_property_name_ex(Z_STR_P(key), &unmangled_class, &unmangled_prop, &unmangled_prop_len) == FAILURE)) {
557
7
      zval_ptr_dtor_str(key);
558
7
      return -1;
559
7
    }
560
561
950k
    if (unmangled_class == NULL) {
562
879k
      existing_propinfo = zend_hash_find_ptr(&ce->properties_info, Z_STR_P(key));
563
879k
    } else {
564
71.4k
      if (!strcmp(unmangled_class, "*")
565
43.8k
       || !strcasecmp(unmangled_class, ZSTR_VAL(ce->name))) {
566
28.2k
        existing_propinfo = zend_hash_str_find_ptr(
567
28.2k
          &ce->properties_info, unmangled_prop, unmangled_prop_len);
568
28.2k
      }
569
71.4k
    }
570
571
950k
    if (existing_propinfo != NULL) {
572
206k
      if (!(existing_propinfo->flags & ZEND_ACC_VIRTUAL)) {
573
206k
        zval_ptr_dtor_str(key);
574
206k
        ZVAL_STR_COPY(key, existing_propinfo->name);
575
206k
        return 1;
576
206k
      } else {
577
0
        php_error_docref(NULL, E_WARNING,
578
0
          "Cannot unserialize value for virtual property %s::$%s",
579
0
          ZSTR_VAL(existing_propinfo->ce->name), Z_STRVAL_P(key));
580
0
        zval_ptr_dtor_str(key);
581
0
        return -1;
582
0
      }
583
206k
    }
584
950k
  }
585
1.37M
  return 0;
586
1.58M
}
587
588
589
/* The members that incomplete_classes_only mode writes itself: the class name of
590
 * every object, and the payload kept for C: and E: entries. Data that already
591
 * carries one of them cannot be told apart from that bookkeeping, so it is
592
 * rejected rather than silently overwritten. */
593
static zend_always_inline bool is_incomplete_class_reserved_member(const zend_string *name)
594
0
{
595
0
  return zend_string_equals_literal(name, MAGIC_MEMBER)
596
0
    || zend_string_equals_literal(name, MAGIC_MEMBER_SERIALIZED)
597
0
    || zend_string_equals_literal(name, MAGIC_MEMBER_ENUM_CASE);
598
0
}
599
600
static zend_always_inline int process_nested_object_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, zend_object *obj)
601
315k
{
602
315k
  if (var_hash) {
603
315k
    if ((*var_hash)->max_depth > 0 && (*var_hash)->cur_depth >= (*var_hash)->max_depth) {
604
3
      php_error_docref(NULL, E_WARNING,
605
3
        "Maximum depth of " ZEND_LONG_FMT " exceeded. "
606
3
        "The depth limit can be changed using the max_depth unserialize() option "
607
3
        "or the unserialize_max_depth ini setting",
608
3
        (*var_hash)->max_depth);
609
3
      return 0;
610
3
    }
611
315k
    (*var_hash)->cur_depth++;
612
315k
  }
613
614
1.64M
  while (elements-- > 0) {
615
1.58M
    zval key, *data;
616
1.58M
    zend_property_info *info = NULL;
617
618
1.58M
    ZVAL_UNDEF(&key);
619
620
1.58M
    if (!php_var_unserialize_internal(&key, p, max, NULL)) {
621
1.13k
      zval_ptr_dtor(&key);
622
1.13k
      goto failure;
623
1.13k
    }
624
625
1.58M
    if (UNEXPECTED(var_hash && (*var_hash)->incomplete_classes_only)
626
0
     && Z_TYPE(key) == IS_STRING
627
0
     && UNEXPECTED(is_incomplete_class_reserved_member(Z_STR(key)))) {
628
0
      zval_ptr_dtor(&key);
629
0
      goto failure;
630
0
    }
631
632
1.58M
    if (EXPECTED(Z_TYPE(key) == IS_STRING)) {
633
1.58M
string_key:
634
1.58M
      data = zend_hash_find(ht, Z_STR(key));
635
1.58M
      if (data != NULL) {
636
435k
        if (Z_TYPE_P(data) == IS_INDIRECT) {
637
206k
declared_property:
638
          /* This is a property with a declaration */
639
206k
          data = Z_INDIRECT_P(data);
640
206k
          info = zend_get_typed_property_info_for_slot(obj, data);
641
206k
          if (info) {
642
206k
            if (Z_ISREF_P(data)) {
643
              /* If the value is overwritten, remove old type source from ref. */
644
9.66k
              ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(data), info);
645
9.66k
            }
646
647
206k
            if ((*var_hash)->ref_props) {
648
              /* Remove old entry from ref_props table, if it exists. */
649
186k
              zend_hash_index_del(
650
186k
                (*var_hash)->ref_props, (uintptr_t) data);
651
186k
            }
652
206k
          }
653
          /* We may override default property value, but they are usually immutable */
654
206k
          if (Z_REFCOUNTED_P(data)) {
655
68.0k
            var_push_dtor_value(var_hash, data);
656
68.0k
          }
657
206k
          ZVAL_NULL(data);
658
435k
        } else {
659
          /* Unusual override of dynamic property */
660
435k
          int ret = is_property_visibility_changed(obj->ce, &key);
661
662
435k
          if (ret > 0) {
663
0
            goto second_try;
664
435k
          } else if (!ret) {
665
435k
            var_push_dtor_value(var_hash, data);
666
435k
            ZVAL_NULL(data);
667
435k
          } else if (ret < 0) {
668
0
            goto failure;
669
0
          }
670
435k
        }
671
1.15M
      } else {
672
1.15M
        int ret = is_property_visibility_changed(obj->ce, &key);
673
674
1.15M
        if (EXPECTED(!ret)) {
675
943k
          if (UNEXPECTED(obj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) {
676
3
            zend_throw_error(NULL, "Cannot create dynamic property %s::$%s",
677
3
              ZSTR_VAL(obj->ce->name), zend_get_unmangled_property_name(Z_STR_P(&key)));
678
3
            zval_ptr_dtor_str(&key);
679
3
            goto failure;
680
943k
          } else if (!(obj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) {
681
461k
            zend_error(E_DEPRECATED, "Creation of dynamic property %s::$%s is deprecated",
682
461k
              ZSTR_VAL(obj->ce->name), zend_get_unmangled_property_name(Z_STR_P(&key)));
683
461k
            if (EG(exception)) {
684
0
              zval_ptr_dtor_str(&key);
685
0
              goto failure;
686
0
            }
687
461k
          }
688
689
943k
          data = zend_hash_add_new(ht, Z_STR(key), &EG(uninitialized_zval));
690
943k
        } else if (ret < 0) {
691
7
          goto failure;
692
206k
        } else {
693
206k
second_try:
694
206k
          data = zend_hash_lookup(ht, Z_STR(key));
695
206k
          if (Z_TYPE_P(data) == IS_INDIRECT) {
696
206k
            goto declared_property;
697
206k
          } else if (UNEXPECTED(Z_TYPE_INFO_P(data) != IS_NULL)) {
698
0
            var_push_dtor_value(var_hash, data);
699
0
            ZVAL_NULL(data);
700
0
          }
701
206k
        }
702
1.15M
      }
703
1.58M
      zval_ptr_dtor_str(&key);
704
1.58M
    } else if (Z_TYPE(key) == IS_LONG) {
705
      /* object properties should include no integers */
706
1.11M
      convert_to_string(&key);
707
1.11M
      goto string_key;
708
1.11M
    } else {
709
111
      zval_ptr_dtor(&key);
710
111
      goto failure;
711
111
    }
712
713
1.58M
    if (!php_var_unserialize_internal(data, p, max, var_hash)) {
714
255k
      if (info) {
715
14.7k
        var_restore_prop_default(var_hash, obj, info, data);
716
14.7k
      }
717
255k
      goto failure;
718
255k
    }
719
720
1.33M
    if (UNEXPECTED(info)) {
721
191k
      if (!zend_verify_prop_assignable_by_ref(info, data, /* strict */ 1)) {
722
39
        var_restore_prop_default(var_hash, obj, info, data);
723
39
        goto failure;
724
39
      }
725
726
191k
      if (Z_ISREF_P(data)) {
727
54.0k
        ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), info);
728
137k
      } else {
729
        /* Remember to which property this slot belongs, so we can add a
730
         * type source if it is turned into a reference lateron. */
731
137k
        if (!(*var_hash)->ref_props) {
732
424
          (*var_hash)->ref_props = emalloc(sizeof(HashTable));
733
424
          zend_hash_init((*var_hash)->ref_props, 8, NULL, NULL, 0);
734
424
        }
735
137k
        zend_hash_index_update_ptr(
736
137k
          (*var_hash)->ref_props, (uintptr_t) data, info);
737
137k
      }
738
191k
    }
739
740
1.33M
    if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
741
0
      (*p)--;
742
0
      goto failure;
743
0
    }
744
745
1.33M
    if (ZEND_HAS_OOM()) {
746
0
      goto failure;
747
0
    }
748
1.33M
  }
749
750
58.9k
  if (var_hash) {
751
58.9k
    (*var_hash)->cur_depth--;
752
58.9k
  }
753
58.9k
  return 1;
754
755
256k
failure:
756
256k
  if (var_hash) {
757
256k
    (*var_hash)->cur_depth--;
758
256k
  }
759
256k
  return 0;
760
315k
}
761
762
static inline int finish_nested_data(UNSERIALIZE_PARAMETER)
763
587k
{
764
587k
  if (*p >= max || **p != '}') {
765
114k
    return 0;
766
114k
  }
767
768
473k
  (*p)++;
769
473k
  return 1;
770
587k
}
771
772
static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce, bool keep_payload)
773
189k
{
774
189k
  zend_long datalen;
775
776
189k
  datalen = parse_iv2((*p) + 2, p);
777
778
189k
  if (max - (*p) < 2) {
779
199
    return 0;
780
199
  }
781
782
188k
  if ((*p)[0] != ':') {
783
37
    return 0;
784
37
  }
785
786
188k
  if ((*p)[1] != '{') {
787
29
    (*p) += 1;
788
29
    return 0;
789
29
  }
790
791
188k
  (*p) += 2;
792
793
188k
  if (datalen < 0 || (max - (*p)) <= datalen) {
794
298
    zend_error(E_WARNING, "Insufficient data for unserializing - " ZEND_LONG_FMT " required, " ZEND_LONG_FMT " present", datalen, (zend_long)(max - (*p)));
795
298
    return 0;
796
298
  }
797
798
  /* Check that '}' is present before calling ce->unserialize() to mitigate issues
799
   * with unserialize reading past the end of the passed buffer if the string is not
800
   * appropriately terminated (usually NUL terminated, but '}' is also sufficient.) */
801
188k
  if ((*p)[datalen] != '}') {
802
35
    (*p) += datalen;
803
35
    return 0;
804
35
  }
805
806
188k
  if (keep_payload) {
807
    /* The class was not resolved (see php_var_unserialize_set_incomplete_classes_only()):
808
     * keep the payload verbatim, so that the object serializes back to the same bytes. */
809
0
    zval payload;
810
811
0
    ZEND_ASSERT(ce == PHP_IC_ENTRY);
812
0
    if (UNEXPECTED(!ZVAL_FSTRINGL(&payload, (const char *) *p, datalen))) {
813
0
      return 0;
814
0
    }
815
0
    object_init_ex(rval, ce);
816
0
    zend_hash_str_update(Z_OBJPROP_P(rval), MAGIC_MEMBER_SERIALIZED, sizeof(MAGIC_MEMBER_SERIALIZED) - 1, &payload);
817
188k
  } else if (ce->unserialize == NULL) {
818
217
    zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name));
819
    /* __PHP_Incomplete_Class has no internal state, an empty instance is safe. */
820
217
    if (ce != PHP_IC_ENTRY) {
821
4
      return 0;
822
4
    }
823
213
    object_init_ex(rval, ce);
824
188k
  } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) {
825
204
    return 0;
826
204
  }
827
828
188k
  if (ZEND_HAS_OOM()) {
829
0
    return 0;
830
0
  }
831
832
188k
  (*p) += datalen + 1; /* +1 for '}' */
833
188k
  return 1;
834
188k
}
835
836
#ifdef PHP_WIN32
837
# pragma optimize("", off)
838
#endif
839
static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, bool has_unserialize)
840
806k
{
841
806k
  HashTable *ht;
842
806k
  bool has_wakeup;
843
844
806k
  if (has_unserialize) {
845
491k
    zval ary, *tmp;
846
847
491k
    if (elements >= HT_MAX_SIZE) {
848
0
      return 0;
849
0
    }
850
851
    /* Avoid reallocation due to packed -> mixed conversion. */
852
491k
    if (UNEXPECTED(!array_finit_size_mixed(&ary, elements))) {
853
0
      return 0;
854
0
    }
855
491k
    if (!process_nested_array_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL(ary), elements)) {
856
67.9k
      ZVAL_DEREF(rval);
857
67.9k
      GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
858
67.9k
      zval_ptr_dtor(&ary);
859
67.9k
      return 0;
860
67.9k
    }
861
862
    /* Delay __unserialize() call until end of serialization. We use two slots here to
863
     * store both the object and the unserialized data array. */
864
423k
    ZVAL_DEREF(rval);
865
423k
    tmp = tmp_var(var_hash, 2);
866
423k
    ZVAL_COPY(tmp, rval);
867
423k
    Z_EXTRA_P(tmp) = VAR_UNSERIALIZE_FLAG;
868
423k
    tmp++;
869
423k
    ZVAL_COPY_VALUE(tmp, &ary);
870
871
423k
    return finish_nested_data(UNSERIALIZE_PASSTHRU);
872
491k
  }
873
874
315k
  has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY
875
163k
    && zend_hash_exists(&Z_OBJCE_P(rval)->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP));
876
877
315k
  ht = Z_OBJPROP_P(rval);
878
315k
  if (elements >= (zend_long)(HT_MAX_SIZE - zend_hash_num_elements(ht))) {
879
0
    return 0;
880
0
  }
881
882
315k
  if (UNEXPECTED(zend_hash_fextend(ht, zend_hash_num_elements(ht) + elements, HT_IS_PACKED(ht)) == FAILURE)) {
883
0
    return 0;
884
0
  }
885
315k
  if (!process_nested_object_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJ_P(rval))) {
886
256k
    if (has_wakeup) {
887
124k
      ZVAL_DEREF(rval);
888
124k
      GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
889
124k
    }
890
256k
    return 0;
891
256k
  }
892
893
58.9k
  ZVAL_DEREF(rval);
894
58.9k
  if (has_wakeup) {
895
    /* Delay __wakeup call until end of serialization */
896
35.2k
    zval *wakeup_var = var_tmp_var(var_hash);
897
35.2k
    ZVAL_COPY(wakeup_var, rval);
898
35.2k
    Z_EXTRA_P(wakeup_var) = VAR_WAKEUP_FLAG;
899
35.2k
  }
900
901
58.9k
  return finish_nested_data(UNSERIALIZE_PASSTHRU);
902
315k
}
903
#ifdef PHP_WIN32
904
# pragma optimize("", on)
905
#endif
906
907
PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
908
130k
{
909
130k
  var_entries *orig_var_entries = (*var_hash)->last;
910
130k
  zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0;
911
130k
  int result;
912
913
130k
  result = php_var_unserialize_internal(UNSERIALIZE_PASSTHRU);
914
915
130k
  if (!result) {
916
    /* If the unserialization failed, mark all elements that have been added to var_hash
917
     * as NULL. This will forbid their use by other unserialize() calls in the same
918
     * unserialization context. */
919
127k
    var_entries *e = orig_var_entries;
920
127k
    zend_long s = orig_used_slots;
921
256k
    while (e) {
922
3.46M
      for (; s < e->used_slots; s++) {
923
3.34M
        e->data[s] = NULL;
924
3.34M
      }
925
926
129k
      e = e->next;
927
129k
      s = 0;
928
129k
    }
929
127k
  }
930
931
130k
  return result;
932
130k
}
933
934
static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
935
7.56M
{
936
7.56M
  const unsigned char *cursor, *limit, *marker, *start;
937
7.56M
  zval *rval_ref;
938
939
7.56M
  limit = max;
940
7.56M
  cursor = *p;
941
942
7.56M
  if (YYCURSOR >= YYLIMIT) {
943
1.72k
    return 0;
944
1.72k
  }
945
946
7.56M
  if (var_hash && (*p)[0] != 'R') {
947
3.34M
    var_push(var_hash, rval);
948
3.34M
  }
949
950
7.56M
  start = cursor;
951
952
953
7.56M
#line 954 "ext/standard/var_unserializer.c"
954
7.56M
{
955
7.56M
  YYCTYPE yych;
956
7.56M
  static const unsigned char yybm[] = {
957
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
958
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
959
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
960
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
961
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
962
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
963
7.56M
    128, 128, 128, 128, 128, 128, 128, 128, 
964
7.56M
    128, 128,   0,   0,   0,   0,   0,   0, 
965
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
966
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
967
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
968
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
969
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
970
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
971
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
972
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
973
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
974
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
975
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
976
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
977
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
978
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
979
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
980
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
981
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
982
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
983
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
984
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
985
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
986
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
987
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
988
7.56M
      0,   0,   0,   0,   0,   0,   0,   0, 
989
7.56M
  };
990
7.56M
  if ((YYLIMIT - YYCURSOR) < 7) YYFILL(7);
991
7.56M
  yych = *YYCURSOR;
992
7.56M
  switch (yych) {
993
189k
    case 'C':
994
998k
    case 'O': goto yy3;
995
465
    case 'E': goto yy4;
996
39.0k
    case 'N': goto yy5;
997
497k
    case 'R': goto yy6;
998
428k
    case 'S': goto yy7;
999
460k
    case 'a': goto yy8;
1000
1.27k
    case 'b': goto yy9;
1001
891k
    case 'd': goto yy10;
1002
3.12M
    case 'i': goto yy11;
1003
421k
    case 'r': goto yy12;
1004
698k
    case 's': goto yy13;
1005
66
    case '}': goto yy14;
1006
1.18k
    default: goto yy1;
1007
7.56M
  }
1008
1.18k
yy1:
1009
1.18k
  ++YYCURSOR;
1010
5.45k
yy2:
1011
5.45k
#line 1553 "ext/standard/var_unserializer.re"
1012
5.45k
  { return 0; }
1013
0
#line 1014 "ext/standard/var_unserializer.c"
1014
998k
yy3:
1015
998k
  yych = *(YYMARKER = ++YYCURSOR);
1016
998k
  if (yych == ':') goto yy15;
1017
136
  goto yy2;
1018
465
yy4:
1019
465
  yych = *(YYMARKER = ++YYCURSOR);
1020
465
  if (yych == ':') goto yy17;
1021
22
  goto yy2;
1022
39.0k
yy5:
1023
39.0k
  yych = *++YYCURSOR;
1024
39.0k
  if (yych == ';') goto yy18;
1025
27
  goto yy2;
1026
497k
yy6:
1027
497k
  yych = *(YYMARKER = ++YYCURSOR);
1028
497k
  if (yych == ':') goto yy19;
1029
54
  goto yy2;
1030
428k
yy7:
1031
428k
  yych = *(YYMARKER = ++YYCURSOR);
1032
428k
  if (yych == ':') goto yy20;
1033
75
  goto yy2;
1034
460k
yy8:
1035
460k
  yych = *(YYMARKER = ++YYCURSOR);
1036
460k
  if (yych == ':') goto yy21;
1037
102
  goto yy2;
1038
1.27k
yy9:
1039
1.27k
  yych = *(YYMARKER = ++YYCURSOR);
1040
1.27k
  if (yych == ':') goto yy22;
1041
21
  goto yy2;
1042
891k
yy10:
1043
891k
  yych = *(YYMARKER = ++YYCURSOR);
1044
891k
  if (yych == ':') goto yy23;
1045
119
  goto yy2;
1046
3.12M
yy11:
1047
3.12M
  yych = *(YYMARKER = ++YYCURSOR);
1048
3.12M
  if (yych == ':') goto yy24;
1049
513
  goto yy2;
1050
421k
yy12:
1051
421k
  yych = *(YYMARKER = ++YYCURSOR);
1052
421k
  if (yych == ':') goto yy25;
1053
65
  goto yy2;
1054
698k
yy13:
1055
698k
  yych = *(YYMARKER = ++YYCURSOR);
1056
698k
  if (yych == ':') goto yy26;
1057
72
  goto yy2;
1058
72
yy14:
1059
66
  ++YYCURSOR;
1060
66
#line 1547 "ext/standard/var_unserializer.re"
1061
66
  {
1062
  /* this is the case where we have less data than planned */
1063
66
  php_error_docref(NULL, E_WARNING, "Unexpected end of serialized data");
1064
66
  return 0; /* not sure if it should be 0 or 1 here? */
1065
698k
}
1066
0
#line 1067 "ext/standard/var_unserializer.c"
1067
998k
yy15:
1068
998k
  yych = *++YYCURSOR;
1069
998k
  if (yybm[0+yych] & 128) {
1070
998k
    goto yy27;
1071
998k
  }
1072
3.06k
yy16:
1073
3.06k
  YYCURSOR = YYMARKER;
1074
3.06k
  goto yy2;
1075
443
yy17:
1076
443
  yych = *++YYCURSOR;
1077
443
  if (yych <= '/') goto yy16;
1078
430
  if (yych <= '9') goto yy28;
1079
14
  goto yy16;
1080
39.0k
yy18:
1081
39.0k
  ++YYCURSOR;
1082
39.0k
#line 1014 "ext/standard/var_unserializer.re"
1083
39.0k
  {
1084
39.0k
  *p = YYCURSOR;
1085
39.0k
  ZVAL_NULL(rval);
1086
39.0k
  return 1;
1087
430
}
1088
0
#line 1089 "ext/standard/var_unserializer.c"
1089
497k
yy19:
1090
497k
  yych = *++YYCURSOR;
1091
497k
  if (yych <= '/') goto yy16;
1092
497k
  if (yych <= '9') goto yy29;
1093
24
  goto yy16;
1094
428k
yy20:
1095
428k
  yych = *++YYCURSOR;
1096
428k
  if (yych <= '/') goto yy16;
1097
428k
  if (yych <= '9') goto yy30;
1098
20
  goto yy16;
1099
459k
yy21:
1100
459k
  yych = *++YYCURSOR;
1101
459k
  if (yych <= '/') goto yy16;
1102
459k
  if (yych <= '9') goto yy31;
1103
47
  goto yy16;
1104
1.25k
yy22:
1105
1.25k
  yych = *++YYCURSOR;
1106
1.25k
  if (yych <= '/') goto yy16;
1107
1.24k
  if (yych <= '0') goto yy32;
1108
699
  if (yych <= '1') goto yy33;
1109
19
  goto yy16;
1110
891k
yy23:
1111
891k
  yych = *++YYCURSOR;
1112
891k
  if (yych <= '/') {
1113
211k
    if (yych <= ',') {
1114
46.4k
      if (yych == '+') goto yy34;
1115
48
      goto yy16;
1116
165k
    } else {
1117
165k
      if (yych <= '-') goto yy35;
1118
98.7k
      if (yych <= '.') goto yy36;
1119
2
      goto yy16;
1120
98.7k
    }
1121
679k
  } else {
1122
679k
    if (yych <= 'I') {
1123
679k
      if (yych <= '9') goto yy37;
1124
638
      if (yych <= 'H') goto yy16;
1125
620
      goto yy38;
1126
638
    } else {
1127
252
      if (yych == 'N') goto yy39;
1128
27
      goto yy16;
1129
252
    }
1130
679k
  }
1131
3.12M
yy24:
1132
3.12M
  yych = *++YYCURSOR;
1133
3.12M
  if (yych <= ',') {
1134
985
    if (yych == '+') goto yy40;
1135
128
    goto yy16;
1136
3.12M
  } else {
1137
3.12M
    if (yych <= '-') goto yy40;
1138
3.02M
    if (yych <= '/') goto yy16;
1139
3.02M
    if (yych <= '9') goto yy41;
1140
201
    goto yy16;
1141
3.02M
  }
1142
421k
yy25:
1143
421k
  yych = *++YYCURSOR;
1144
421k
  if (yych <= '/') goto yy16;
1145
421k
  if (yych <= '9') goto yy42;
1146
18
  goto yy16;
1147
697k
yy26:
1148
697k
  yych = *++YYCURSOR;
1149
697k
  if (yych <= '/') goto yy16;
1150
697k
  if (yych <= '9') goto yy43;
1151
27
  goto yy16;
1152
1.84M
yy27:
1153
1.84M
  ++YYCURSOR;
1154
1.84M
  if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
1155
1.84M
  yych = *YYCURSOR;
1156
1.84M
  if (yybm[0+yych] & 128) {
1157
842k
    goto yy27;
1158
842k
  }
1159
998k
  if (yych <= '/') goto yy16;
1160
997k
  if (yych <= ':') goto yy44;
1161
82
  goto yy16;
1162
4.78k
yy28:
1163
4.78k
  ++YYCURSOR;
1164
4.78k
  if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
1165
4.78k
  yych = *YYCURSOR;
1166
4.78k
  if (yych <= '/') goto yy16;
1167
4.75k
  if (yych <= '9') goto yy28;
1168
382
  if (yych <= ':') goto yy45;
1169
16
  goto yy16;
1170
507k
yy29:
1171
507k
  ++YYCURSOR;
1172
507k
  if (YYLIMIT <= YYCURSOR) YYFILL(1);
1173
507k
  yych = *YYCURSOR;
1174
507k
  if (yych <= '/') goto yy16;
1175
507k
  if (yych <= '9') goto yy29;
1176
497k
  if (yych == ';') goto yy46;
1177
32
  goto yy16;
1178
507k
yy30:
1179
507k
  ++YYCURSOR;
1180
507k
  if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
1181
507k
  yych = *YYCURSOR;
1182
507k
  if (yych <= '/') goto yy16;
1183
507k
  if (yych <= '9') goto yy30;
1184
428k
  if (yych <= ':') goto yy47;
1185
33
  goto yy16;
1186
541k
yy31:
1187
541k
  ++YYCURSOR;
1188
541k
  if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
1189
541k
  yych = *YYCURSOR;
1190
541k
  if (yych <= '/') goto yy16;
1191
541k
  if (yych <= '9') goto yy31;
1192
459k
  if (yych <= ':') goto yy48;
1193
55
  goto yy16;
1194
547
yy32:
1195
547
  yych = *++YYCURSOR;
1196
547
  if (yych == ';') goto yy49;
1197
20
  goto yy16;
1198
680
yy33:
1199
680
  yych = *++YYCURSOR;
1200
680
  if (yych == ';') goto yy50;
1201
18
  goto yy16;
1202
46.4k
yy34:
1203
46.4k
  yych = *++YYCURSOR;
1204
46.4k
  if (yych == '.') goto yy36;
1205
40.6k
  if (yych <= '/') goto yy16;
1206
40.6k
  if (yych <= '9') goto yy37;
1207
14
  goto yy16;
1208
66.7k
yy35:
1209
66.7k
  yych = *++YYCURSOR;
1210
66.7k
  if (yych <= '/') {
1211
4.11k
    if (yych != '.') goto yy16;
1212
62.5k
  } else {
1213
62.5k
    if (yych <= '9') goto yy37;
1214
318
    if (yych == 'I') goto yy38;
1215
22
    goto yy16;
1216
318
  }
1217
108k
yy36:
1218
108k
  yych = *++YYCURSOR;
1219
108k
  if (yych <= '/') goto yy16;
1220
108k
  if (yych <= '9') goto yy51;
1221
13
  goto yy16;
1222
14.9M
yy37:
1223
14.9M
  ++YYCURSOR;
1224
14.9M
  if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3);
1225
14.9M
  yych = *YYCURSOR;
1226
14.9M
  if (yych <= ':') {
1227
14.4M
    if (yych <= '.') {
1228
204k
      if (yych <= '-') goto yy16;
1229
204k
      goto yy51;
1230
14.2M
    } else {
1231
14.2M
      if (yych <= '/') goto yy16;
1232
14.2M
      if (yych <= '9') goto yy37;
1233
39
      goto yy16;
1234
14.2M
    }
1235
14.4M
  } else {
1236
576k
    if (yych <= 'E') {
1237
493k
      if (yych <= ';') goto yy52;
1238
109k
      if (yych <= 'D') goto yy16;
1239
109k
      goto yy53;
1240
109k
    } else {
1241
83.4k
      if (yych == 'e') goto yy53;
1242
81
      goto yy16;
1243
83.4k
    }
1244
576k
  }
1245
916
yy38:
1246
916
  yych = *++YYCURSOR;
1247
916
  if (yych == 'N') goto yy54;
1248
26
  goto yy16;
1249
225
yy39:
1250
225
  yych = *++YYCURSOR;
1251
225
  if (yych == 'A') goto yy55;
1252
21
  goto yy16;
1253
102k
yy40:
1254
102k
  yych = *++YYCURSOR;
1255
102k
  if (yych <= '/') goto yy16;
1256
102k
  if (yych >= ':') goto yy16;
1257
9.74M
yy41:
1258
9.74M
  ++YYCURSOR;
1259
9.74M
  if (YYLIMIT <= YYCURSOR) YYFILL(1);
1260
9.74M
  yych = *YYCURSOR;
1261
9.74M
  if (yych <= '/') goto yy16;
1262
9.74M
  if (yych <= '9') goto yy41;
1263
3.12M
  if (yych == ';') goto yy56;
1264
235
  goto yy16;
1265
503k
yy42:
1266
503k
  ++YYCURSOR;
1267
503k
  if (YYLIMIT <= YYCURSOR) YYFILL(1);
1268
503k
  yych = *YYCURSOR;
1269
503k
  if (yych <= '/') goto yy16;
1270
503k
  if (yych <= '9') goto yy42;
1271
421k
  if (yych == ';') goto yy57;
1272
25
  goto yy16;
1273
1.06M
yy43:
1274
1.06M
  ++YYCURSOR;
1275
1.06M
  if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
1276
1.06M
  yych = *YYCURSOR;
1277
1.06M
  if (yych <= '/') goto yy16;
1278
1.06M
  if (yych <= '9') goto yy43;
1279
697k
  if (yych <= ':') goto yy58;
1280
31
  goto yy16;
1281
997k
yy44:
1282
997k
  yych = *++YYCURSOR;
1283
997k
  if (yych == '"') goto yy59;
1284
86
  goto yy16;
1285
366
yy45:
1286
366
  yych = *++YYCURSOR;
1287
366
  if (yych == '"') goto yy60;
1288
16
  goto yy16;
1289
497k
yy46:
1290
497k
  ++YYCURSOR;
1291
497k
#line 958 "ext/standard/var_unserializer.re"
1292
497k
  {
1293
497k
  zend_long id;
1294
1295
497k
  *p = YYCURSOR;
1296
497k
  if (!var_hash) return 0;
1297
1298
497k
  id = parse_uiv(start + 2) - 1;
1299
497k
  if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
1300
253
    return 0;
1301
253
  }
1302
1303
496k
  if (rval_ref == rval || (Z_ISREF_P(rval_ref) && Z_REFVAL_P(rval_ref) == rval)) {
1304
2
    return 0;
1305
2
  }
1306
1307
496k
  if (!Z_ISREF_P(rval_ref)) {
1308
13.0k
    zend_property_info *info = NULL;
1309
13.0k
    if ((*var_hash)->ref_props) {
1310
826
      info = zend_hash_index_find_ptr((*var_hash)->ref_props, (uintptr_t) rval_ref);
1311
826
    }
1312
13.0k
    ZVAL_NEW_REF(rval_ref, rval_ref);
1313
13.0k
    if (info) {
1314
116
      ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(rval_ref), info);
1315
116
    }
1316
13.0k
  }
1317
1318
496k
  ZVAL_COPY(rval, rval_ref);
1319
1320
496k
  return 1;
1321
496k
}
1322
0
#line 1323 "ext/standard/var_unserializer.c"
1323
428k
yy47:
1324
428k
  yych = *++YYCURSOR;
1325
428k
  if (yych == '"') goto yy61;
1326
31
  goto yy16;
1327
459k
yy48:
1328
459k
  yych = *++YYCURSOR;
1329
459k
  if (yych == '{') goto yy62;
1330
74
  goto yy16;
1331
527
yy49:
1332
527
  ++YYCURSOR;
1333
527
#line 1020 "ext/standard/var_unserializer.re"
1334
527
  {
1335
527
  *p = YYCURSOR;
1336
527
  ZVAL_FALSE(rval);
1337
527
  return 1;
1338
459k
}
1339
0
#line 1340 "ext/standard/var_unserializer.c"
1340
662
yy50:
1341
662
  ++YYCURSOR;
1342
662
#line 1026 "ext/standard/var_unserializer.re"
1343
662
  {
1344
662
  *p = YYCURSOR;
1345
662
  ZVAL_TRUE(rval);
1346
662
  return 1;
1347
459k
}
1348
0
#line 1349 "ext/standard/var_unserializer.c"
1349
40.6M
yy51:
1350
40.6M
  ++YYCURSOR;
1351
40.6M
  if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3);
1352
40.6M
  yych = *YYCURSOR;
1353
40.6M
  if (yych <= ';') {
1354
40.5M
    if (yych <= '/') goto yy16;
1355
40.5M
    if (yych <= '9') goto yy51;
1356
225k
    if (yych <= ':') goto yy16;
1357
225k
  } else {
1358
87.6k
    if (yych <= 'E') {
1359
2.81k
      if (yych <= 'D') goto yy16;
1360
2.81k
      goto yy53;
1361
84.8k
    } else {
1362
84.8k
      if (yych == 'e') goto yy53;
1363
65
      goto yy16;
1364
84.8k
    }
1365
87.6k
  }
1366
889k
yy52:
1367
889k
  ++YYCURSOR;
1368
889k
#line 1074 "ext/standard/var_unserializer.re"
1369
889k
  {
1370
#if SIZEOF_ZEND_LONG == 4
1371
use_double:
1372
#endif
1373
889k
  *p = YYCURSOR;
1374
889k
  ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
1375
889k
  return 1;
1376
40.6M
}
1377
0
#line 1378 "ext/standard/var_unserializer.c"
1378
280k
yy53:
1379
280k
  yych = *++YYCURSOR;
1380
280k
  if (yych <= ',') {
1381
1.31k
    if (yych == '+') goto yy63;
1382
36
    goto yy16;
1383
278k
  } else {
1384
278k
    if (yych <= '-') goto yy63;
1385
95.9k
    if (yych <= '/') goto yy16;
1386
95.9k
    if (yych <= '9') goto yy64;
1387
30
    goto yy16;
1388
95.9k
  }
1389
890
yy54:
1390
890
  yych = *++YYCURSOR;
1391
890
  if (yych == 'F') goto yy65;
1392
17
  goto yy16;
1393
204
yy55:
1394
204
  yych = *++YYCURSOR;
1395
204
  if (yych == 'N') goto yy65;
1396
14
  goto yy16;
1397
3.12M
yy56:
1398
3.12M
  ++YYCURSOR;
1399
3.12M
#line 1032 "ext/standard/var_unserializer.re"
1400
3.12M
  {
1401
#if SIZEOF_ZEND_LONG == 4
1402
  int digits = YYCURSOR - start - 3;
1403
1404
  if (start[2] == '-' || start[2] == '+') {
1405
    digits--;
1406
  }
1407
1408
  /* Use double for large zend_long values that were serialized on a 64-bit system */
1409
  if (digits >= MAX_LENGTH_OF_LONG - 1) {
1410
    if (digits == MAX_LENGTH_OF_LONG - 1) {
1411
      int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1);
1412
1413
      if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) {
1414
        goto use_double;
1415
      }
1416
    } else {
1417
      goto use_double;
1418
    }
1419
  }
1420
#endif
1421
3.12M
  *p = YYCURSOR;
1422
3.12M
  ZVAL_LONG(rval, parse_iv(start + 2));
1423
3.12M
  return 1;
1424
204
}
1425
0
#line 1426 "ext/standard/var_unserializer.c"
1426
421k
yy57:
1427
421k
  ++YYCURSOR;
1428
421k
#line 989 "ext/standard/var_unserializer.re"
1429
421k
  {
1430
421k
  zend_long id;
1431
1432
421k
  *p = YYCURSOR;
1433
421k
  if (!var_hash) return 0;
1434
1435
421k
  id = parse_uiv(start + 2) - 1;
1436
421k
  if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
1437
344
    return 0;
1438
344
  }
1439
1440
421k
  if (rval_ref == rval) {
1441
3
    return 0;
1442
3
  }
1443
1444
421k
  ZVAL_DEREF(rval_ref);
1445
421k
  if (Z_TYPE_P(rval_ref) != IS_OBJECT) {
1446
10
    return 0;
1447
10
  }
1448
1449
421k
  ZVAL_COPY(rval, rval_ref);
1450
1451
421k
  return 1;
1452
421k
}
1453
0
#line 1454 "ext/standard/var_unserializer.c"
1454
697k
yy58:
1455
697k
  yych = *++YYCURSOR;
1456
697k
  if (yych == '"') goto yy66;
1457
40
  goto yy16;
1458
997k
yy59:
1459
997k
  ++YYCURSOR;
1460
997k
#line 1200 "ext/standard/var_unserializer.re"
1461
997k
  {
1462
997k
  size_t len, maxlen;
1463
997k
  zend_long elements;
1464
997k
  char *str;
1465
997k
  zend_string *class_name;
1466
997k
  zend_class_entry *ce;
1467
997k
  bool incomplete_class = 0;
1468
997k
  bool custom_object = 0;
1469
997k
  bool has_unserialize = 0;
1470
1471
997k
  zval user_func;
1472
997k
  zval retval;
1473
997k
  zval args[1];
1474
1475
997k
    if (!var_hash) return 0;
1476
997k
  if (*start == 'C') {
1477
189k
    custom_object = 1;
1478
189k
  }
1479
1480
997k
  len = parse_uiv(start + 2);
1481
997k
  maxlen = max - YYCURSOR;
1482
997k
  if (maxlen < len || len == 0) {
1483
319
    *p = start + 2;
1484
319
    return 0;
1485
319
  }
1486
1487
997k
  str = (char*)YYCURSOR;
1488
1489
997k
  YYCURSOR += len;
1490
1491
997k
  if (*(YYCURSOR) != '"') {
1492
322
    *p = YYCURSOR;
1493
322
    return 0;
1494
322
  }
1495
997k
  if (*(YYCURSOR+1) != ':') {
1496
51
    *p = YYCURSOR+1;
1497
51
    return 0;
1498
51
  }
1499
1500
997k
  if (len == 0) {
1501
    /* empty class names are not allowed */
1502
0
    return 0;
1503
0
  }
1504
1505
997k
  if (str[0] == '\000') {
1506
    /* runtime definition keys are not allowed */
1507
2
    return 0;
1508
2
  }
1509
1510
997k
  if (str[0] == '\\') {
1511
    /* class name can't start from namespace separator */
1512
2
    return 0;
1513
2
  }
1514
1515
997k
  class_name = zend_string_finit_existing_interned(str, len, /* permanent */ false, /* fallible */ true);
1516
997k
  if (UNEXPECTED(!class_name)) {
1517
0
    return 0;
1518
0
  }
1519
1520
997k
  do {
1521
997k
    zend_string *lc_name;
1522
1523
997k
    if ((*var_hash)->incomplete_classes_only) {
1524
      /* Do not look the class up at all, so that no user code can run. */
1525
0
      if (!zend_is_valid_class_name(class_name)) {
1526
0
        zend_string_release_ex(class_name, 0);
1527
0
        return 0;
1528
0
      }
1529
0
      incomplete_class = 1;
1530
0
      ce = PHP_IC_ENTRY;
1531
0
      break;
1532
0
    }
1533
1534
997k
    if (!(*var_hash)->allowed_classes && ZSTR_HAS_CE_CACHE(class_name)) {
1535
0
      ce = ZSTR_GET_CE_CACHE(class_name);
1536
0
      if (ce) {
1537
0
        break;
1538
0
      }
1539
0
    }
1540
1541
997k
    lc_name = zend_string_ftolower(class_name);
1542
997k
    if (UNEXPECTED(!lc_name)) {
1543
0
      zend_string_release_ex(class_name, 0);
1544
0
      return 0;
1545
0
    }
1546
997k
    if(!unserialize_allowed_class(lc_name, var_hash)) {
1547
0
      zend_string_release_ex(lc_name, 0);
1548
0
      if (!zend_is_valid_class_name(class_name)) {
1549
0
        zend_string_release_ex(class_name, 0);
1550
0
        return 0;
1551
0
      }
1552
0
      incomplete_class = 1;
1553
0
      ce = PHP_IC_ENTRY;
1554
0
      break;
1555
0
    }
1556
1557
997k
    if ((*var_hash)->allowed_classes && ZSTR_HAS_CE_CACHE(class_name)) {
1558
0
      ce = ZSTR_GET_CE_CACHE(class_name);
1559
0
      if (ce) {
1560
0
        zend_string_release_ex(lc_name, 0);
1561
0
        break;
1562
0
      }
1563
0
    }
1564
1565
997k
    ce = zend_hash_find_ptr(EG(class_table), lc_name);
1566
997k
    if (ce
1567
843k
     && (ce->ce_flags & ZEND_ACC_LINKED)
1568
843k
     && !(ce->ce_flags & ZEND_ACC_ANON_CLASS)) {
1569
843k
      zend_string_release_ex(lc_name, 0);
1570
843k
      break;
1571
843k
    }
1572
1573
154k
    if (!ZSTR_HAS_CE_CACHE(class_name) && !zend_is_valid_class_name(class_name)) {
1574
123
      zend_string_release_ex(lc_name, 0);
1575
123
      zend_string_release_ex(class_name, 0);
1576
123
      return 0;
1577
123
    }
1578
1579
    /* Try to find class directly */
1580
153k
    BG(serialize_lock)++;
1581
153k
    ce = zend_lookup_class_ex(class_name, lc_name, 0);
1582
153k
    BG(serialize_lock)--;
1583
153k
    zend_string_release_ex(lc_name, 0);
1584
153k
    if (EG(exception)) {
1585
0
      zend_string_release_ex(class_name, 0);
1586
0
      return 0;
1587
0
    }
1588
1589
153k
    if (ce) {
1590
0
      break;
1591
0
    }
1592
1593
    /* Check for unserialize callback */
1594
153k
    if (PG(unserialize_callback_func) == NULL || zend_string_equals(PG(unserialize_callback_func), zend_empty_string)) {
1595
153k
      incomplete_class = 1;
1596
153k
      ce = PHP_IC_ENTRY;
1597
153k
      break;
1598
153k
    }
1599
1600
    /* Call unserialize callback */
1601
0
    ZVAL_STR(&user_func, zend_string_dup(PG(unserialize_callback_func), false));
1602
1603
0
    ZVAL_STR(&args[0], class_name);
1604
0
    BG(serialize_lock)++;
1605
0
    call_user_function(NULL, NULL, &user_func, &retval, 1, args);
1606
0
    BG(serialize_lock)--;
1607
0
    zval_ptr_dtor(&retval);
1608
1609
0
    if (EG(exception)) {
1610
0
      zend_string_release_ex(class_name, 0);
1611
0
      zval_ptr_dtor(&user_func);
1612
0
      return 0;
1613
0
    }
1614
1615
    /* The callback function may have defined the class */
1616
0
    BG(serialize_lock)++;
1617
0
    if ((ce = zend_lookup_class(class_name)) == NULL) {
1618
0
      php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", Z_STRVAL(user_func));
1619
0
      incomplete_class = 1;
1620
0
      ce = PHP_IC_ENTRY;
1621
0
    }
1622
0
    BG(serialize_lock)--;
1623
1624
0
    zval_ptr_dtor(&user_func);
1625
0
  } while (0);
1626
1627
996k
  *p = YYCURSOR;
1628
1629
996k
  if (ce->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) {
1630
7
    zend_throw_exception_ex(NULL, 0, "Unserialization of '%s' is not allowed",
1631
7
      ZSTR_VAL(ce->name));
1632
7
    zend_string_release_ex(class_name, 0);
1633
7
    return 0;
1634
7
  }
1635
1636
996k
  if (custom_object) {
1637
189k
    int ret;
1638
1639
189k
    ret = object_custom(UNSERIALIZE_PASSTHRU, ce, (*var_hash)->incomplete_classes_only);
1640
1641
189k
    if (ret && incomplete_class) {
1642
213
      php_store_class_name(rval, class_name);
1643
213
    }
1644
189k
    zend_string_release_ex(class_name, 0);
1645
189k
    return ret;
1646
189k
  }
1647
1648
807k
  if (*p >= max - 2) {
1649
243
    zend_error(E_WARNING, "Bad unserialize data");
1650
243
    zend_string_release_ex(class_name, 0);
1651
243
    return 0;
1652
243
  }
1653
1654
807k
  elements = parse_iv2(*p + 2, p);
1655
807k
  if (elements < 0 || IS_FAKE_ELEM_COUNT(elements, max - YYCURSOR)) {
1656
550
    zend_string_release_ex(class_name, 0);
1657
550
    return 0;
1658
550
  }
1659
1660
807k
  YYCURSOR = *p;
1661
1662
807k
  if (*(YYCURSOR) != ':') {
1663
116
    zend_string_release_ex(class_name, 0);
1664
116
    return 0;
1665
116
  }
1666
807k
  if (*(YYCURSOR+1) != '{') {
1667
50
    *p = YYCURSOR+1;
1668
50
    zend_string_release_ex(class_name, 0);
1669
50
    return 0;
1670
50
  }
1671
1672
806k
  *p += 2;
1673
1674
806k
  has_unserialize = !incomplete_class && ce->__unserialize;
1675
1676
  /* If this class implements Serializable, it should not land here but in object_custom().
1677
   * The passed string obviously doesn't descend from the regular serializer. However, if
1678
   * there is both Serializable::unserialize() and __unserialize(), then both may be used,
1679
   * depending on the serialization format. */
1680
806k
  if (ce->serialize != NULL && !has_unserialize) {
1681
0
    zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ZSTR_VAL(ce->name));
1682
0
    zend_string_release_ex(class_name, 0);
1683
0
    return 0;
1684
0
  }
1685
1686
806k
  if (object_init_ex(rval, ce) == FAILURE) {
1687
8
    zend_string_release_ex(class_name, 0);
1688
8
    return 0;
1689
8
  }
1690
1691
806k
  if (ZEND_HAS_OOM()) {
1692
0
    zend_string_release_ex(class_name, 0);
1693
0
    return 0;
1694
0
  }
1695
1696
806k
  if (incomplete_class) {
1697
152k
    php_store_class_name(rval, class_name);
1698
152k
  }
1699
806k
  zend_string_release_ex(class_name, 0);
1700
1701
806k
  return object_common(UNSERIALIZE_PASSTHRU, elements, has_unserialize);
1702
806k
}
1703
0
#line 1704 "ext/standard/var_unserializer.c"
1704
350
yy60:
1705
350
  ++YYCURSOR;
1706
350
#line 1443 "ext/standard/var_unserializer.re"
1707
350
  {
1708
350
  if (!var_hash) return 0;
1709
1710
348
  size_t len = parse_uiv(start + 2);
1711
348
  size_t maxlen = max - YYCURSOR;
1712
348
  if (maxlen < len || len == 0) {
1713
239
    *p = start + 2;
1714
239
    return 0;
1715
239
  }
1716
1717
109
  char *str = (char *) YYCURSOR;
1718
109
  YYCURSOR += len;
1719
1720
109
  if (*(YYCURSOR) != '"') {
1721
27
    *p = YYCURSOR;
1722
27
    return 0;
1723
27
  }
1724
82
  if (*(YYCURSOR+1) != ';') {
1725
14
    *p = YYCURSOR+1;
1726
14
    return 0;
1727
14
  }
1728
1729
68
  char *colon_ptr = memchr(str, ':', len);
1730
68
  if (colon_ptr == NULL) {
1731
17
    php_error_docref(NULL, E_WARNING, "Invalid enum name '%.*s' (missing colon)", (int) len, str);
1732
17
    return 0;
1733
17
  }
1734
51
  size_t colon_pos = colon_ptr - str;
1735
1736
51
  zend_string *enum_name = zend_string_init(str, colon_pos, 0);
1737
51
  zend_string *case_name = zend_string_init(&str[colon_pos + 1], len - colon_pos - 1, 0);
1738
1739
51
  if (!zend_is_valid_class_name(enum_name)) {
1740
2
    goto fail;
1741
2
  }
1742
1743
49
  if ((*var_hash)->incomplete_classes_only) {
1744
    /* Do not load the enum: keep the case name, so that the value serializes
1745
     * back to the same bytes. */
1746
0
    zval case_zv;
1747
1748
0
    YYCURSOR += 2;
1749
0
    *p = YYCURSOR;
1750
1751
0
    object_init_ex(rval, PHP_IC_ENTRY);
1752
0
    if (ZEND_HAS_OOM()) {
1753
0
      goto fail;
1754
0
    }
1755
0
    php_store_class_name(rval, enum_name);
1756
0
    ZVAL_STR(&case_zv, case_name);
1757
0
    zend_hash_str_update(Z_OBJPROP_P(rval), MAGIC_MEMBER_ENUM_CASE, sizeof(MAGIC_MEMBER_ENUM_CASE) - 1, &case_zv);
1758
0
    zend_string_release_ex(enum_name, 0);
1759
0
    return 1;
1760
0
  }
1761
1762
49
  BG(serialize_lock)++;
1763
49
  zend_class_entry *ce = zend_lookup_class(enum_name);
1764
49
  BG(serialize_lock)--;
1765
49
  if (!ce) {
1766
45
    php_error_docref(NULL, E_WARNING, "Class '%s' not found", ZSTR_VAL(enum_name));
1767
45
    goto fail;
1768
45
  }
1769
4
  if (!(ce->ce_flags & ZEND_ACC_ENUM)) {
1770
4
    php_error_docref(NULL, E_WARNING, "Class '%s' is not an enum", ZSTR_VAL(enum_name));
1771
4
    goto fail;
1772
4
  }
1773
1774
0
  YYCURSOR += 2;
1775
0
  *p = YYCURSOR;
1776
1777
0
  zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), case_name);
1778
0
  if (!c) {
1779
0
    php_error_docref(NULL, E_WARNING, "Undefined constant %s::%s", ZSTR_VAL(enum_name), ZSTR_VAL(case_name));
1780
0
    goto fail;
1781
0
  }
1782
1783
0
  if (!(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE)) {
1784
0
    php_error_docref(NULL, E_WARNING, "%s::%s is not an enum case", ZSTR_VAL(enum_name), ZSTR_VAL(case_name));
1785
0
    goto fail;
1786
0
  }
1787
1788
0
  zend_string_release_ex(enum_name, 0);
1789
0
  zend_string_release_ex(case_name, 0);
1790
1791
0
  zval *value = &c->value;
1792
0
  if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
1793
0
    BG(serialize_lock)++;
1794
0
    zend_result result = zval_update_constant_ex(value, c->ce);
1795
0
    BG(serialize_lock)--;
1796
0
    if (result == FAILURE) {
1797
0
      return 0;
1798
0
    }
1799
0
  }
1800
0
  ZEND_ASSERT(Z_TYPE_P(value) == IS_OBJECT);
1801
0
  ZVAL_COPY(rval, value);
1802
1803
0
  return 1;
1804
1805
51
fail:
1806
51
  zend_string_release_ex(enum_name, 0);
1807
51
  zend_string_release_ex(case_name, 0);
1808
51
  return 0;
1809
0
}
1810
0
#line 1811 "ext/standard/var_unserializer.c"
1811
428k
yy61:
1812
428k
  ++YYCURSOR;
1813
428k
#line 1129 "ext/standard/var_unserializer.re"
1814
428k
  {
1815
428k
  size_t len, maxlen;
1816
428k
  zend_string *str;
1817
1818
428k
  len = parse_uiv(start + 2);
1819
428k
  maxlen = max - YYCURSOR;
1820
428k
  if (maxlen < len) {
1821
216
    *p = start + 2;
1822
216
    return 0;
1823
216
  }
1824
1825
427k
  if ((str = unserialize_str(&YYCURSOR, len, maxlen)) == NULL) {
1826
84
    return 0;
1827
84
  }
1828
1829
427k
  if (*(YYCURSOR) != '"') {
1830
133
    zend_string_efree(str);
1831
133
    *p = YYCURSOR;
1832
133
    return 0;
1833
133
  }
1834
1835
427k
  if (*(YYCURSOR + 1) != ';') {
1836
20
    efree(str);
1837
20
    *p = YYCURSOR + 1;
1838
20
    return 0;
1839
20
  }
1840
1841
427k
  YYCURSOR += 2;
1842
427k
  *p = YYCURSOR;
1843
1844
427k
  ZVAL_STR(rval, str);
1845
1846
427k
  php_error_docref(NULL, E_DEPRECATED, "Unserializing the 'S' format is deprecated");
1847
1848
427k
  return 1;
1849
427k
}
1850
0
#line 1851 "ext/standard/var_unserializer.c"
1851
459k
yy62:
1852
459k
  ++YYCURSOR;
1853
459k
#line 1166 "ext/standard/var_unserializer.re"
1854
459k
  {
1855
459k
  zend_long elements = parse_iv(start + 2);
1856
  /* use iv() not uiv() in order to check data range */
1857
459k
  *p = YYCURSOR;
1858
459k
    if (!var_hash) return 0;
1859
1860
459k
  if (elements < 0 || elements >= HT_MAX_SIZE || IS_FAKE_ELEM_COUNT(elements, max - YYCURSOR)) {
1861
404
    return 0;
1862
404
  }
1863
1864
459k
  if (elements) {
1865
    /* we can't convert from packed to hash during unserialization, because
1866
       reference to some zvals might be kept in var_hash (to support references) */
1867
418k
    if (UNEXPECTED(!array_finit_size_mixed(rval, elements))) {
1868
0
      return 0;
1869
0
    }
1870
418k
  } else {
1871
41.2k
    ZVAL_EMPTY_ARRAY(rval);
1872
41.2k
    return finish_nested_data(UNSERIALIZE_PASSTHRU);
1873
41.2k
  }
1874
1875
  /* The array may contain references to itself, in which case we'll be modifying an
1876
   * rc>1 array. This is okay, since the array is, ostensibly, only visible to
1877
   * unserialize (in practice unserialization handlers also see it). Ideally we should
1878
   * prohibit "r:" references to non-objects, as we only generate them for objects. */
1879
459k
  HT_ALLOW_COW_VIOLATION(Z_ARRVAL_P(rval));
1880
1881
418k
  if (!process_nested_array_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements)) {
1882
353k
    return 0;
1883
353k
  }
1884
1885
64.1k
  return finish_nested_data(UNSERIALIZE_PASSTHRU);
1886
418k
}
1887
0
#line 1888 "ext/standard/var_unserializer.c"
1888
184k
yy63:
1889
184k
  yych = *++YYCURSOR;
1890
184k
  if (yych <= '/') goto yy16;
1891
184k
  if (yych >= ':') goto yy16;
1892
1.76M
yy64:
1893
1.76M
  ++YYCURSOR;
1894
1.76M
  if (YYLIMIT <= YYCURSOR) YYFILL(1);
1895
1.76M
  yych = *YYCURSOR;
1896
1.76M
  if (yych <= '/') goto yy16;
1897
1.76M
  if (yych <= '9') goto yy64;
1898
280k
  if (yych == ';') goto yy52;
1899
33
  goto yy16;
1900
1.06k
yy65:
1901
1.06k
  yych = *++YYCURSOR;
1902
1.06k
  if (yych == ';') goto yy67;
1903
20
  goto yy16;
1904
697k
yy66:
1905
697k
  ++YYCURSOR;
1906
697k
#line 1083 "ext/standard/var_unserializer.re"
1907
697k
  {
1908
697k
  size_t len, maxlen;
1909
697k
  char *str;
1910
1911
697k
  len = parse_uiv(start + 2);
1912
697k
  maxlen = max - YYCURSOR;
1913
697k
  if (maxlen < len) {
1914
244
    *p = start + 2;
1915
244
    return 0;
1916
244
  }
1917
1918
697k
  str = (char*)YYCURSOR;
1919
1920
697k
  YYCURSOR += len;
1921
1922
697k
  if (*(YYCURSOR) != '"') {
1923
119
    *p = YYCURSOR;
1924
119
    return 0;
1925
119
  }
1926
1927
697k
  if (*(YYCURSOR + 1) != ';') {
1928
17
    *p = YYCURSOR + 1;
1929
17
    return 0;
1930
17
  }
1931
1932
697k
  YYCURSOR += 2;
1933
697k
  *p = YYCURSOR;
1934
1935
697k
  zend_string *zstr;
1936
1937
697k
  if (!var_hash) {
1938
    /* Array or object key unserialization */
1939
561k
    zstr = zend_string_finit_existing_interned(str, len, /* permanent */ false, /* fallible */ true);
1940
561k
  } else {
1941
135k
    zstr = zend_string_finit_fast(str, len, /* fallible */ true);
1942
135k
  }
1943
1944
697k
  if (UNEXPECTED(!zstr)) {
1945
0
    return 0;
1946
0
  }
1947
1948
697k
  ZVAL_STR(rval, zstr);
1949
1950
697k
  return 1;
1951
697k
}
1952
0
#line 1953 "ext/standard/var_unserializer.c"
1953
1.04k
yy67:
1954
1.04k
  ++YYCURSOR;
1955
1.04k
#line 1058 "ext/standard/var_unserializer.re"
1956
1.04k
  {
1957
1.04k
  *p = YYCURSOR;
1958
1959
1.04k
  if (!strncmp((char*)start + 2, "NAN", 3)) {
1960
174
    ZVAL_DOUBLE(rval, ZEND_NAN);
1961
869
  } else if (!strncmp((char*)start + 2, "INF", 3)) {
1962
578
    ZVAL_DOUBLE(rval, ZEND_INFINITY);
1963
578
  } else if (!strncmp((char*)start + 2, "-INF", 4)) {
1964
291
    ZVAL_DOUBLE(rval, -ZEND_INFINITY);
1965
291
  } else {
1966
0
    ZVAL_NULL(rval);
1967
0
  }
1968
1969
1.04k
  return 1;
1970
697k
}
1971
697k
#line 1972 "ext/standard/var_unserializer.c"
1972
697k
}
1973
0
#line 1555 "ext/standard/var_unserializer.re"
1974
1975
1976
0
  return 0;
1977
697k
}