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