1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 7                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2015 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/* {{{ reference-handling for unserializer: var_* */
26#define VAR_ENTRIES_MAX 1024
27#define VAR_ENTRIES_DBG 0
28
29typedef struct {
30    zval *data[VAR_ENTRIES_MAX];
31    zend_long used_slots;
32    void *next;
33} var_entries;
34
35typedef struct {
36    zval data[VAR_ENTRIES_MAX];
37    zend_long used_slots;
38    void *next;
39} var_dtor_entries;
40
41static inline void var_push(php_unserialize_data_t *var_hashx, zval *rval)
42{
43    var_entries *var_hash = (*var_hashx)->last;
44#if VAR_ENTRIES_DBG
45    fprintf(stderr, "var_push(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
46#endif
47
48    if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
49        var_hash = emalloc(sizeof(var_entries));
50        var_hash->used_slots = 0;
51        var_hash->next = 0;
52
53        if (!(*var_hashx)->first) {
54            (*var_hashx)->first = var_hash;
55        } else {
56            ((var_entries *) (*var_hashx)->last)->next = var_hash;
57        }
58
59        (*var_hashx)->last = var_hash;
60    }
61
62    var_hash->data[var_hash->used_slots++] = rval;
63}
64
65PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval *rval)
66{
67    var_dtor_entries *var_hash;
68
69    if (!var_hashx || !*var_hashx) {
70        return;
71    }
72
73    var_hash = (*var_hashx)->last_dtor;
74#if VAR_ENTRIES_DBG
75    fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
76#endif
77
78    if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
79        var_hash = emalloc(sizeof(var_dtor_entries));
80        var_hash->used_slots = 0;
81        var_hash->next = 0;
82
83        if (!(*var_hashx)->first_dtor) {
84            (*var_hashx)->first_dtor = var_hash;
85        } else {
86            ((var_dtor_entries *) (*var_hashx)->last_dtor)->next = var_hash;
87        }
88
89        (*var_hashx)->last_dtor = var_hash;
90    }
91
92    ZVAL_COPY(&var_hash->data[var_hash->used_slots], rval);
93    var_hash->used_slots++;
94}
95
96//???
97#if 0
98PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval *rval)
99{
100    var_dtor_entries *var_hash = (*var_hashx)->last_dtor;
101#if VAR_ENTRIES_DBG
102    fprintf(stderr, "var_push_dtor_no_addref(%ld): %d (%d)\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval));
103#endif
104
105    if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
106        var_hash = emalloc(sizeof(var_dtor_entries));
107        var_hash->used_slots = 0;
108        var_hash->next = 0;
109
110        if (!(*var_hashx)->first_dtor) {
111            (*var_hashx)->first_dtor = var_hash;
112        } else {
113            ((var_entries *) (*var_hashx)->last_dtor)->next = var_hash;
114        }
115
116        (*var_hashx)->last_dtor = var_hash;
117    }
118
119    ZVAL_COPY_VALUE(&var_hash->data[var_hash->used_slots], rval);
120    var_hash->used_slots++;
121}
122#endif
123
124PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval *nzval)
125{
126    zend_long i;
127    var_entries *var_hash = (*var_hashx)->first;
128#if VAR_ENTRIES_DBG
129    fprintf(stderr, "var_replace(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(nzval));
130#endif
131
132    while (var_hash) {
133        for (i = 0; i < var_hash->used_slots; i++) {
134            if (var_hash->data[i] == ozval) {
135                var_hash->data[i] = nzval;
136                /* do not break here */
137            }
138        }
139        var_hash = var_hash->next;
140    }
141}
142
143static zval *var_access(php_unserialize_data_t *var_hashx, zend_long id)
144{
145    var_entries *var_hash = (*var_hashx)->first;
146#if VAR_ENTRIES_DBG
147    fprintf(stderr, "var_access(%ld): %ld\n", var_hash?var_hash->used_slots:-1L, id);
148#endif
149
150    while (id >= VAR_ENTRIES_MAX && var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) {
151        var_hash = var_hash->next;
152        id -= VAR_ENTRIES_MAX;
153    }
154
155    if (!var_hash) return NULL;
156
157    if (id < 0 || id >= var_hash->used_slots) return NULL;
158
159    return var_hash->data[id];
160}
161
162PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
163{
164    void *next;
165    zend_long i;
166    var_entries *var_hash = (*var_hashx)->first;
167    var_dtor_entries *var_dtor_hash = (*var_hashx)->first_dtor;
168#if VAR_ENTRIES_DBG
169    fprintf(stderr, "var_destroy(%ld)\n", var_hash?var_hash->used_slots:-1L);
170#endif
171
172    while (var_hash) {
173        next = var_hash->next;
174        efree(var_hash);
175        var_hash = next;
176    }
177
178    while (var_dtor_hash) {
179        for (i = 0; i < var_dtor_hash->used_slots; i++) {
180            zval_ptr_dtor(&var_dtor_hash->data[i]);
181        }
182        next = var_dtor_hash->next;
183        efree(var_dtor_hash);
184        var_dtor_hash = next;
185    }
186}
187
188/* }}} */
189
190static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t maxlen)
191{
192    size_t i, j;
193    zend_string *str = zend_string_alloc(len, 0);
194    unsigned char *end = *(unsigned char **)p+maxlen;
195
196    if (end < *p) {
197        zend_string_free(str);
198        return NULL;
199    }
200
201    for (i = 0; i < len; i++) {
202        if (*p >= end) {
203            zend_string_free(str);
204            return NULL;
205        }
206        if (**p != '\\') {
207            ZSTR_VAL(str)[i] = (char)**p;
208        } else {
209            unsigned char ch = 0;
210
211            for (j = 0; j < 2; j++) {
212                (*p)++;
213                if (**p >= '0' && **p <= '9') {
214                    ch = (ch << 4) + (**p -'0');
215                } else if (**p >= 'a' && **p <= 'f') {
216                    ch = (ch << 4) + (**p -'a'+10);
217                } else if (**p >= 'A' && **p <= 'F') {
218                    ch = (ch << 4) + (**p -'A'+10);
219                } else {
220                    zend_string_free(str);
221                    return NULL;
222                }
223            }
224            ZSTR_VAL(str)[i] = (char)ch;
225        }
226        (*p)++;
227    }
228    ZSTR_VAL(str)[i] = 0;
229    ZSTR_LEN(str) = i;
230    return str;
231}
232
233static inline int unserialize_allowed_class(zend_string *class_name, HashTable *classes)
234{
235    zend_string *lcname;
236    int res;
237    ALLOCA_FLAG(use_heap)
238
239    if(classes == NULL) {
240        return 1;
241    }
242    if(!zend_hash_num_elements(classes)) {
243        return 0;
244    }
245
246    ZSTR_ALLOCA_ALLOC(lcname, ZSTR_LEN(class_name), use_heap);
247    zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(class_name), ZSTR_LEN(class_name));
248    res = zend_hash_exists(classes, lcname);
249    ZSTR_ALLOCA_FREE(lcname, use_heap);
250    return res;
251}
252
253#define YYFILL(n) do { } while (0)
254#define YYCTYPE unsigned char
255#define YYCURSOR cursor
256#define YYLIMIT limit
257#define YYMARKER marker
258
259
260/*!re2c
261uiv = [+]? [0-9]+;
262iv = [+-]? [0-9]+;
263nv = [+-]? ([0-9]* "." [0-9]+|[0-9]+ "." [0-9]*);
264nvexp = (iv | nv) [eE] [+-]? iv;
265any = [\000-\377];
266object = [OC];
267*/
268
269
270
271static inline zend_long parse_iv2(const unsigned char *p, const unsigned char **q)
272{
273    char cursor;
274    zend_long result = 0;
275    int neg = 0;
276
277    switch (*p) {
278        case '-':
279            neg++;
280            /* fall-through */
281        case '+':
282            p++;
283    }
284
285    while (1) {
286        cursor = (char)*p;
287        if (cursor >= '0' && cursor <= '9') {
288            result = result * 10 + (size_t)(cursor - (unsigned char)'0');
289        } else {
290            break;
291        }
292        p++;
293    }
294    if (q) *q = p;
295    if (neg) return -result;
296    return result;
297}
298
299static inline zend_long parse_iv(const unsigned char *p)
300{
301    return parse_iv2(p, NULL);
302}
303
304/* no need to check for length - re2c already did */
305static inline size_t parse_uiv(const unsigned char *p)
306{
307    unsigned char cursor;
308    size_t result = 0;
309
310    if (*p == '+') {
311        p++;
312    }
313
314    while (1) {
315        cursor = *p;
316        if (cursor >= '0' && cursor <= '9') {
317            result = result * 10 + (size_t)(cursor - (unsigned char)'0');
318        } else {
319            break;
320        }
321        p++;
322    }
323    return result;
324}
325
326#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes
327#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes
328
329static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops)
330{
331    while (elements-- > 0) {
332        zval key, *data, d, *old_data;
333        zend_ulong idx;
334
335        ZVAL_UNDEF(&key);
336
337        if (!php_var_unserialize_ex(&key, p, max, NULL, classes)) {
338            zval_dtor(&key);
339            return 0;
340        }
341
342        data = NULL;
343        ZVAL_UNDEF(&d);
344
345        if (!objprops) {
346            if (Z_TYPE(key) == IS_LONG) {
347                idx = Z_LVAL(key);
348numeric_key:
349                if (UNEXPECTED((old_data = zend_hash_index_find(ht, idx)) != NULL)) {
350                    //??? update hash
351                    var_push_dtor(var_hash, old_data);
352                    data = zend_hash_index_update(ht, idx, &d);
353                } else {
354                    data = zend_hash_index_add_new(ht, idx, &d);
355                }
356            } else if (Z_TYPE(key) == IS_STRING) {
357                if (UNEXPECTED(ZEND_HANDLE_NUMERIC(Z_STR(key), idx))) {
358                    goto numeric_key;
359                }
360                if (UNEXPECTED((old_data = zend_hash_find(ht, Z_STR(key))) != NULL)) {
361                    //??? update hash
362                    var_push_dtor(var_hash, old_data);
363                    data = zend_hash_update(ht, Z_STR(key), &d);
364                } else {
365                    data = zend_hash_add_new(ht, Z_STR(key), &d);
366                }
367            } else {
368                zval_dtor(&key);
369                return 0;
370            }
371        } else {
372            if (EXPECTED(Z_TYPE(key) == IS_STRING)) {
373string_key:
374                if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
375                    if (Z_TYPE_P(old_data) == IS_INDIRECT) {
376                        old_data = Z_INDIRECT_P(old_data);
377                    }
378                    var_push_dtor(var_hash, old_data);
379                    data = zend_hash_update_ind(ht, Z_STR(key), &d);
380                } else {
381                    data = zend_hash_add_new(ht, Z_STR(key), &d);
382                }
383            } else if (Z_TYPE(key) == IS_LONG) {
384                /* object properties should include no integers */
385                convert_to_string(&key);
386                goto string_key;
387            } else {
388                zval_dtor(&key);
389                return 0;
390            }
391        }
392
393        zval_dtor(&key);
394
395        if (!php_var_unserialize_ex(data, p, max, var_hash, classes)) {
396            return 0;
397        }
398
399        var_push_dtor(var_hash, data);
400
401        if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
402            (*p)--;
403            return 0;
404        }
405    }
406
407    return 1;
408}
409
410static inline int finish_nested_data(UNSERIALIZE_PARAMETER)
411{
412    if (*((*p)++) == '}')
413        return 1;
414
415#if SOMETHING_NEW_MIGHT_LEAD_TO_CRASH_ENABLE_IF_YOU_ARE_BRAVE
416    zval_ptr_dtor(rval);
417#endif
418    return 0;
419}
420
421static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
422{
423    zend_long datalen;
424
425    datalen = parse_iv2((*p) + 2, p);
426
427    (*p) += 2;
428
429    if (datalen < 0 || (max - (*p)) <= datalen) {
430        zend_error(E_WARNING, "Insufficient data for unserializing - %pd required, %pd present", datalen, (zend_long)(max - (*p)));
431        return 0;
432    }
433
434    if (ce->unserialize == NULL) {
435        zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name));
436        object_init_ex(rval, ce);
437    } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) {
438        return 0;
439    }
440
441    (*p) += datalen;
442
443    return finish_nested_data(UNSERIALIZE_PASSTHRU);
444}
445
446static inline zend_long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
447{
448    zend_long elements;
449
450    elements = parse_iv2((*p) + 2, p);
451
452    (*p) += 2;
453
454    if (ce->serialize == NULL) {
455        object_init_ex(rval, ce);
456    } else {
457        /* If this class implements Serializable, it should not land here but in object_custom(). The passed string
458        obviously doesn't descend from the regular serializer. */
459        zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ZSTR_VAL(ce->name));
460        return 0;
461    }
462
463    return elements;
464}
465
466#ifdef PHP_WIN32
467# pragma optimize("", off)
468#endif
469static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
470{
471    zval retval;
472    zval fname;
473
474    if (Z_TYPE_P(rval) != IS_OBJECT) {
475        return 0;
476    }
477
478    //??? TODO: resize before
479    if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_P(rval), elements, 1)) {
480        return 0;
481    }
482
483    ZVAL_DEREF(rval);
484    if (Z_OBJCE_P(rval) != PHP_IC_ENTRY &&
485        zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1)) {
486        ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1);
487        BG(serialize_lock)++;
488        call_user_function_ex(CG(function_table), rval, &fname, &retval, 0, 0, 1, NULL);
489        BG(serialize_lock)--;
490        zval_dtor(&fname);
491        zval_dtor(&retval);
492    }
493
494    if (EG(exception)) {
495        return 0;
496    }
497
498    return finish_nested_data(UNSERIALIZE_PASSTHRU);
499
500}
501#ifdef PHP_WIN32
502# pragma optimize("", on)
503#endif
504
505PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash)
506{
507    HashTable *classes = NULL;
508    return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU);
509}
510
511
512PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER)
513{
514    const unsigned char *cursor, *limit, *marker, *start;
515    zval *rval_ref;
516
517    limit = max;
518    cursor = *p;
519
520    if (YYCURSOR >= YYLIMIT) {
521        return 0;
522    }
523
524    if (var_hash && (*p)[0] != 'R') {
525        var_push(var_hash, rval);
526    }
527
528    start = cursor;
529
530/*!re2c
531
532"R:" iv ";"     {
533    zend_long id;
534
535    *p = YYCURSOR;
536    if (!var_hash) return 0;
537
538    id = parse_iv(start + 2) - 1;
539    if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
540        return 0;
541    }
542
543    zval_ptr_dtor(rval);
544    if (Z_ISREF_P(rval_ref)) {
545        ZVAL_COPY(rval, rval_ref);
546    } else {
547        ZVAL_NEW_REF(rval_ref, rval_ref);
548        ZVAL_COPY(rval, rval_ref);
549    }
550
551    return 1;
552}
553
554"r:" iv ";"     {
555    zend_long id;
556
557    *p = YYCURSOR;
558    if (!var_hash) return 0;
559
560    id = parse_iv(start + 2) - 1;
561    if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
562        return 0;
563    }
564
565//???
566//???   if (rval == rval_ref) return 0;
567
568//???   if (!ZVAL_IS_UNDEF(rval)) {
569//???       var_push_dtor_no_addref(var_hash, rval);
570//???   }
571    ZVAL_COPY(rval, rval_ref);
572//???   Z_UNSET_ISREF_PP(rval);
573
574    return 1;
575}
576
577"N;"    {
578    *p = YYCURSOR;
579    ZVAL_NULL(rval);
580    return 1;
581}
582
583"b:" [01] ";"   {
584    *p = YYCURSOR;
585    ZVAL_BOOL(rval, parse_iv(start + 2));
586    return 1;
587}
588
589"i:" iv ";" {
590#if SIZEOF_ZEND_LONG == 4
591    int digits = YYCURSOR - start - 3;
592
593    if (start[2] == '-' || start[2] == '+') {
594        digits--;
595    }
596
597    /* Use double for large zend_long values that were serialized on a 64-bit system */
598    if (digits >= MAX_LENGTH_OF_LONG - 1) {
599        if (digits == MAX_LENGTH_OF_LONG - 1) {
600            int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1);
601
602            if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) {
603                goto use_double;
604            }
605        } else {
606            goto use_double;
607        }
608    }
609#endif
610    *p = YYCURSOR;
611    ZVAL_LONG(rval, parse_iv(start + 2));
612    return 1;
613}
614
615"d:" ("NAN" | "-"? "INF") ";"   {
616    *p = YYCURSOR;
617
618    if (!strncmp((char*)start + 2, "NAN", 3)) {
619        ZVAL_DOUBLE(rval, php_get_nan());
620    } else if (!strncmp((char*)start + 2, "INF", 3)) {
621        ZVAL_DOUBLE(rval, php_get_inf());
622    } else if (!strncmp((char*)start + 2, "-INF", 4)) {
623        ZVAL_DOUBLE(rval, -php_get_inf());
624    } else {
625        ZVAL_NULL(rval);
626    }
627
628    return 1;
629}
630
631"d:" (iv | nv | nvexp) ";"  {
632#if SIZEOF_ZEND_LONG == 4
633use_double:
634#endif
635    *p = YYCURSOR;
636    ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
637    return 1;
638}
639
640"s:" uiv ":" ["]    {
641    size_t len, maxlen;
642    char *str;
643
644    len = parse_uiv(start + 2);
645    maxlen = max - YYCURSOR;
646    if (maxlen < len) {
647        *p = start + 2;
648        return 0;
649    }
650
651    str = (char*)YYCURSOR;
652
653    YYCURSOR += len;
654
655    if (*(YYCURSOR) != '"') {
656        *p = YYCURSOR;
657        return 0;
658    }
659
660    YYCURSOR += 2;
661    *p = YYCURSOR;
662
663    ZVAL_STRINGL(rval, str, len);
664    return 1;
665}
666
667"S:" uiv ":" ["]    {
668    size_t len, maxlen;
669    zend_string *str;
670
671    len = parse_uiv(start + 2);
672    maxlen = max - YYCURSOR;
673    if (maxlen < len) {
674        *p = start + 2;
675        return 0;
676    }
677
678    if ((str = unserialize_str(&YYCURSOR, len, maxlen)) == NULL) {
679        return 0;
680    }
681
682    if (*(YYCURSOR) != '"') {
683        zend_string_free(str);
684        *p = YYCURSOR;
685        return 0;
686    }
687
688    YYCURSOR += 2;
689    *p = YYCURSOR;
690
691    ZVAL_STR(rval, str);
692    return 1;
693}
694
695"a:" uiv ":" "{" {
696    zend_long elements = parse_iv(start + 2);
697    /* use iv() not uiv() in order to check data range */
698    *p = YYCURSOR;
699
700    if (elements < 0) {
701        return 0;
702    }
703
704    array_init_size(rval, elements);
705//??? we can't convert from packed to hash during unserialization, because
706//??? reference to some zvals might be keept in var_hash (to support references)
707    zend_hash_real_init(Z_ARRVAL_P(rval), 0);
708
709    if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) {
710        return 0;
711    }
712
713    return finish_nested_data(UNSERIALIZE_PASSTHRU);
714}
715
716"o:" iv ":" ["] {
717
718//???   INIT_PZVAL(rval);
719
720    return object_common2(UNSERIALIZE_PASSTHRU,
721            object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
722}
723
724object ":" uiv ":" ["]  {
725    size_t len, len2, len3, maxlen;
726    zend_long elements;
727    char *str;
728    zend_string *class_name;
729    zend_class_entry *ce;
730    int incomplete_class = 0;
731
732    int custom_object = 0;
733
734    zval user_func;
735    zval retval;
736    zval args[1];
737
738    if (*start == 'C') {
739        custom_object = 1;
740    }
741
742//???   INIT_PZVAL(rval);
743    len2 = len = parse_uiv(start + 2);
744    maxlen = max - YYCURSOR;
745    if (maxlen < len || len == 0) {
746        *p = start + 2;
747        return 0;
748    }
749
750    str = (char*)YYCURSOR;
751
752    YYCURSOR += len;
753
754    if (*(YYCURSOR) != '"') {
755        *p = YYCURSOR;
756        return 0;
757    }
758    if (*(YYCURSOR+1) != ':') {
759        *p = YYCURSOR+1;
760        return 0;
761    }
762
763    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\\");
764    if (len3 != len)
765    {
766        *p = YYCURSOR + len3 - len;
767        return 0;
768    }
769
770    class_name = zend_string_init(str, len, 0);
771
772    do {
773        if(!unserialize_allowed_class(class_name, classes)) {
774            incomplete_class = 1;
775            ce = PHP_IC_ENTRY;
776            break;
777        }
778
779        /* Try to find class directly */
780        BG(serialize_lock)++;
781        ce = zend_lookup_class(class_name);
782        if (ce) {
783            BG(serialize_lock)--;
784            if (EG(exception)) {
785                zend_string_release(class_name);
786                return 0;
787            }
788            break;
789        }
790        BG(serialize_lock)--;
791
792        if (EG(exception)) {
793            zend_string_release(class_name);
794            return 0;
795        }
796
797        /* Check for unserialize callback */
798        if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) {
799            incomplete_class = 1;
800            ce = PHP_IC_ENTRY;
801            break;
802        }
803
804        /* Call unserialize callback */
805        ZVAL_STRING(&user_func, PG(unserialize_callback_func));
806
807        ZVAL_STR_COPY(&args[0], class_name);
808        BG(serialize_lock)++;
809        if (call_user_function_ex(CG(function_table), NULL, &user_func, &retval, 1, args, 0, NULL) != SUCCESS) {
810            BG(serialize_lock)--;
811            if (EG(exception)) {
812                zend_string_release(class_name);
813                zval_ptr_dtor(&user_func);
814                zval_ptr_dtor(&args[0]);
815                return 0;
816            }
817            php_error_docref(NULL, E_WARNING, "defined (%s) but not found", Z_STRVAL(user_func));
818            incomplete_class = 1;
819            ce = PHP_IC_ENTRY;
820            zval_ptr_dtor(&user_func);
821            zval_ptr_dtor(&args[0]);
822            break;
823        }
824        BG(serialize_lock)--;
825        zval_ptr_dtor(&retval);
826        if (EG(exception)) {
827            zend_string_release(class_name);
828            zval_ptr_dtor(&user_func);
829            zval_ptr_dtor(&args[0]);
830            return 0;
831        }
832
833        /* The callback function may have defined the class */
834        if ((ce = zend_lookup_class(class_name)) == NULL) {
835            php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", Z_STRVAL(user_func));
836            incomplete_class = 1;
837            ce = PHP_IC_ENTRY;
838        }
839
840        zval_ptr_dtor(&user_func);
841        zval_ptr_dtor(&args[0]);
842        break;
843    } while (1);
844
845    *p = YYCURSOR;
846
847    if (custom_object) {
848        int ret;
849
850        ret = object_custom(UNSERIALIZE_PASSTHRU, ce);
851
852        if (ret && incomplete_class) {
853            php_store_class_name(rval, ZSTR_VAL(class_name), len2);
854        }
855        zend_string_release(class_name);
856        return ret;
857    }
858
859    elements = object_common1(UNSERIALIZE_PASSTHRU, ce);
860
861    if (incomplete_class) {
862        php_store_class_name(rval, ZSTR_VAL(class_name), len2);
863    }
864    zend_string_release(class_name);
865
866    return object_common2(UNSERIALIZE_PASSTHRU, elements);
867}
868
869"}" {
870    /* this is the case where we have less data than planned */
871    php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data");
872    return 0; /* not sure if it should be 0 or 1 here? */
873}
874
875any { return 0; }
876
877*/
878
879    return 0;
880}
881