1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 7                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2014 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_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            str->val[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            str->val[i] = (char)ch;
225        }
226        (*p)++;
227    }
228    str->val[i] = 0;
229    str->len = 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    STR_ALLOCA_ALLOC(lcname, class_name->len, use_heap);
247    zend_str_tolower_copy(lcname->val, class_name->val, class_name->len);
248    res = zend_hash_exists(classes, lcname);
249    STR_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 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
334        ZVAL_UNDEF(&key);
335
336        if (!php_var_unserialize_ex(&key, p, max, NULL, classes)) {
337            zval_dtor(&key);
338            return 0;
339        }
340
341        if (Z_TYPE(key) != IS_LONG && Z_TYPE(key) != IS_STRING) {
342            zval_dtor(&key);
343            return 0;
344        }
345
346        data = NULL;
347        ZVAL_UNDEF(&d);
348
349        if (!objprops) {
350            switch (Z_TYPE(key)) {
351            case IS_LONG:
352                if ((old_data = zend_hash_index_find(ht, Z_LVAL(key))) != NULL) {
353                    //??? update hash
354                    var_push_dtor(var_hash, old_data);
355                }
356                data = zend_hash_index_update(ht, Z_LVAL(key), &d);
357                break;
358            case IS_STRING:
359                if ((old_data = zend_symtable_find(ht, Z_STR(key))) != NULL) {
360                    //??? update hash
361                    var_push_dtor(var_hash, old_data);
362                }
363                data = zend_symtable_update(ht, Z_STR(key), &d);
364                break;
365            }
366        } else {
367            /* object properties should include no integers */
368            convert_to_string(&key);
369            if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
370                if (Z_TYPE_P(old_data) == IS_INDIRECT) {
371                    old_data = Z_INDIRECT_P(old_data);
372                }
373                var_push_dtor(var_hash, old_data);
374            }
375            data = zend_hash_update_ind(ht, Z_STR(key), &d);
376        }
377
378        zval_dtor(&key);
379
380        if (!php_var_unserialize_ex(data, p, max, var_hash, classes)) {
381            return 0;
382        }
383
384        if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
385            (*p)--;
386            return 0;
387        }
388    }
389
390    return 1;
391}
392
393static inline int finish_nested_data(UNSERIALIZE_PARAMETER)
394{
395    if (*((*p)++) == '}')
396        return 1;
397
398#if SOMETHING_NEW_MIGHT_LEAD_TO_CRASH_ENABLE_IF_YOU_ARE_BRAVE
399    zval_ptr_dtor(rval);
400#endif
401    return 0;
402}
403
404static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
405{
406    zend_long datalen;
407
408    datalen = parse_iv2((*p) + 2, p);
409
410    (*p) += 2;
411
412    if (datalen < 0 || (max - (*p)) <= datalen) {
413        zend_error(E_WARNING, "Insufficient data for unserializing - %pd required, %pd present", datalen, (zend_long)(max - (*p)));
414        return 0;
415    }
416
417    if (ce->unserialize == NULL) {
418        zend_error(E_WARNING, "Class %s has no unserializer", ce->name->val);
419        object_init_ex(rval, ce);
420    } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) {
421        return 0;
422    }
423
424    (*p) += datalen;
425
426    return finish_nested_data(UNSERIALIZE_PASSTHRU);
427}
428
429static inline zend_long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
430{
431    zend_long elements;
432
433    elements = parse_iv2((*p) + 2, p);
434
435    (*p) += 2;
436
437    if (ce->serialize == NULL) {
438        object_init_ex(rval, ce);
439    } else {
440        /* If this class implements Serializable, it should not land here but in object_custom(). The passed string
441        obviously doesn't descend from the regular serializer. */
442        zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ce->name->val);
443        return 0;
444    }
445
446    return elements;
447}
448
449#ifdef PHP_WIN32
450# pragma optimize("", off)
451#endif
452static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
453{
454    zval retval;
455    zval fname;
456
457    if (Z_TYPE_P(rval) != IS_OBJECT) {
458        return 0;
459    }
460
461    //??? TODO: resize before
462    if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_P(rval), elements, 1)) {
463        return 0;
464    }
465
466    ZVAL_DEREF(rval);
467    if (Z_OBJCE_P(rval) != PHP_IC_ENTRY &&
468        zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1)) {
469        ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1);
470        BG(serialize_lock)++;
471        call_user_function_ex(CG(function_table), rval, &fname, &retval, 0, 0, 1, NULL);
472        BG(serialize_lock)--;
473        zval_dtor(&fname);
474        zval_dtor(&retval);
475    }
476
477    if (EG(exception)) {
478        return 0;
479    }
480
481    return finish_nested_data(UNSERIALIZE_PASSTHRU);
482
483}
484#ifdef PHP_WIN32
485# pragma optimize("", on)
486#endif
487
488PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash)
489{
490    HashTable *classes = NULL;
491    return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU);
492}
493
494
495PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER)
496{
497    const unsigned char *cursor, *limit, *marker, *start;
498    zval *rval_ref;
499
500    limit = max;
501    cursor = *p;
502
503    if (YYCURSOR >= YYLIMIT) {
504        return 0;
505    }
506
507    if (var_hash && (*p)[0] != 'R') {
508        var_push(var_hash, rval);
509    }
510
511    start = cursor;
512
513/*!re2c
514
515"R:" iv ";"     {
516    zend_long id;
517
518    *p = YYCURSOR;
519    if (!var_hash) return 0;
520
521    id = parse_iv(start + 2) - 1;
522    if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
523        return 0;
524    }
525
526    zval_ptr_dtor(rval);
527    if (Z_ISREF_P(rval_ref)) {
528        ZVAL_COPY(rval, rval_ref);
529    } else {
530        ZVAL_NEW_REF(rval_ref, rval_ref);
531        ZVAL_COPY(rval, rval_ref);
532    }
533
534    return 1;
535}
536
537"r:" iv ";"     {
538    zend_long id;
539
540    *p = YYCURSOR;
541    if (!var_hash) return 0;
542
543    id = parse_iv(start + 2) - 1;
544    if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) {
545        return 0;
546    }
547
548//???
549//???   if (rval == rval_ref) return 0;
550
551//???   if (!ZVAL_IS_UNDEF(rval)) {
552//???       var_push_dtor_no_addref(var_hash, rval);
553//???   }
554    ZVAL_COPY(rval, rval_ref);
555//???   Z_UNSET_ISREF_PP(rval);
556
557    return 1;
558}
559
560"N;"    {
561    *p = YYCURSOR;
562    ZVAL_NULL(rval);
563    return 1;
564}
565
566"b:" [01] ";"   {
567    *p = YYCURSOR;
568    ZVAL_BOOL(rval, parse_iv(start + 2));
569    return 1;
570}
571
572"i:" iv ";" {
573#if SIZEOF_ZEND_LONG == 4
574    int digits = YYCURSOR - start - 3;
575
576    if (start[2] == '-' || start[2] == '+') {
577        digits--;
578    }
579
580    /* Use double for large zend_long values that were serialized on a 64-bit system */
581    if (digits >= MAX_LENGTH_OF_LONG - 1) {
582        if (digits == MAX_LENGTH_OF_LONG - 1) {
583            int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1);
584
585            if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) {
586                goto use_double;
587            }
588        } else {
589            goto use_double;
590        }
591    }
592#endif
593    *p = YYCURSOR;
594    ZVAL_LONG(rval, parse_iv(start + 2));
595    return 1;
596}
597
598"d:" ("NAN" | "-"? "INF") ";"   {
599    *p = YYCURSOR;
600
601    if (!strncmp((char*)start + 2, "NAN", 3)) {
602        ZVAL_DOUBLE(rval, php_get_nan());
603    } else if (!strncmp((char*)start + 2, "INF", 3)) {
604        ZVAL_DOUBLE(rval, php_get_inf());
605    } else if (!strncmp((char*)start + 2, "-INF", 4)) {
606        ZVAL_DOUBLE(rval, -php_get_inf());
607    } else {
608        ZVAL_NULL(rval);
609    }
610
611    return 1;
612}
613
614"d:" (iv | nv | nvexp) ";"  {
615#if SIZEOF_ZEND_LONG == 4
616use_double:
617#endif
618    *p = YYCURSOR;
619    ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
620    return 1;
621}
622
623"s:" uiv ":" ["]    {
624    size_t len, maxlen;
625    char *str;
626
627    len = parse_uiv(start + 2);
628    maxlen = max - YYCURSOR;
629    if (maxlen < len) {
630        *p = start + 2;
631        return 0;
632    }
633
634    str = (char*)YYCURSOR;
635
636    YYCURSOR += len;
637
638    if (*(YYCURSOR) != '"') {
639        *p = YYCURSOR;
640        return 0;
641    }
642
643    YYCURSOR += 2;
644    *p = YYCURSOR;
645
646    ZVAL_STRINGL(rval, str, len);
647    return 1;
648}
649
650"S:" uiv ":" ["]    {
651    size_t len, maxlen;
652    zend_string *str;
653
654    len = parse_uiv(start + 2);
655    maxlen = max - YYCURSOR;
656    if (maxlen < len) {
657        *p = start + 2;
658        return 0;
659    }
660
661    if ((str = unserialize_str(&YYCURSOR, len, maxlen)) == NULL) {
662        return 0;
663    }
664
665    if (*(YYCURSOR) != '"') {
666        zend_string_free(str);
667        *p = YYCURSOR;
668        return 0;
669    }
670
671    YYCURSOR += 2;
672    *p = YYCURSOR;
673
674    ZVAL_STR(rval, str);
675    return 1;
676}
677
678"a:" uiv ":" "{" {
679    zend_long elements = parse_iv(start + 2);
680    /* use iv() not uiv() in order to check data range */
681    *p = YYCURSOR;
682
683    if (elements < 0) {
684        return 0;
685    }
686
687    array_init_size(rval, elements);
688//??? we can't convert from packed to hash during unserialization, because
689//??? reference to some zvals might be keept in var_hash (to support references)
690    zend_hash_real_init(Z_ARRVAL_P(rval), 0);
691
692    if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) {
693        return 0;
694    }
695
696    return finish_nested_data(UNSERIALIZE_PASSTHRU);
697}
698
699"o:" iv ":" ["] {
700
701//???   INIT_PZVAL(rval);
702
703    return object_common2(UNSERIALIZE_PASSTHRU,
704            object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
705}
706
707object ":" uiv ":" ["]  {
708    size_t len, len2, len3, maxlen;
709    zend_long elements;
710    char *str;
711    zend_string *class_name;
712    zend_class_entry *ce;
713    int incomplete_class = 0;
714
715    int custom_object = 0;
716
717    zval user_func;
718    zval retval;
719    zval args[1];
720
721    if (*start == 'C') {
722        custom_object = 1;
723    }
724
725//???   INIT_PZVAL(rval);
726    len2 = len = parse_uiv(start + 2);
727    maxlen = max - YYCURSOR;
728    if (maxlen < len || len == 0) {
729        *p = start + 2;
730        return 0;
731    }
732
733    str = (char*)YYCURSOR;
734
735    YYCURSOR += len;
736
737    if (*(YYCURSOR) != '"') {
738        *p = YYCURSOR;
739        return 0;
740    }
741    if (*(YYCURSOR+1) != ':') {
742        *p = YYCURSOR+1;
743        return 0;
744    }
745
746    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\\");
747    if (len3 != len)
748    {
749        *p = YYCURSOR + len3 - len;
750        return 0;
751    }
752
753    class_name = zend_string_init(str, len, 0);
754
755    do {
756        if(!unserialize_allowed_class(class_name, classes)) {
757            incomplete_class = 1;
758            ce = PHP_IC_ENTRY;
759            break;
760        }
761
762        /* Try to find class directly */
763        BG(serialize_lock)++;
764        ce = zend_lookup_class(class_name);
765        if (ce) {
766            BG(serialize_lock)--;
767            if (EG(exception)) {
768                zend_string_release(class_name);
769                return 0;
770            }
771            break;
772        }
773        BG(serialize_lock)--;
774
775        if (EG(exception)) {
776            zend_string_release(class_name);
777            return 0;
778        }
779
780        /* Check for unserialize callback */
781        if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) {
782            incomplete_class = 1;
783            ce = PHP_IC_ENTRY;
784            break;
785        }
786
787        /* Call unserialize callback */
788        ZVAL_STRING(&user_func, PG(unserialize_callback_func));
789
790        ZVAL_STR_COPY(&args[0], class_name);
791        BG(serialize_lock)++;
792        if (call_user_function_ex(CG(function_table), NULL, &user_func, &retval, 1, args, 0, NULL) != SUCCESS) {
793            BG(serialize_lock)--;
794            if (EG(exception)) {
795                zend_string_release(class_name);
796                zval_ptr_dtor(&user_func);
797                zval_ptr_dtor(&args[0]);
798                return 0;
799            }
800            php_error_docref(NULL, E_WARNING, "defined (%s) but not found", Z_STRVAL(user_func));
801            incomplete_class = 1;
802            ce = PHP_IC_ENTRY;
803            zval_ptr_dtor(&user_func);
804            zval_ptr_dtor(&args[0]);
805            break;
806        }
807        BG(serialize_lock)--;
808        zval_ptr_dtor(&retval);
809        if (EG(exception)) {
810            zend_string_release(class_name);
811            zval_ptr_dtor(&user_func);
812            zval_ptr_dtor(&args[0]);
813            return 0;
814        }
815
816        /* The callback function may have defined the class */
817        if ((ce = zend_lookup_class(class_name)) == NULL) {
818            php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", Z_STRVAL(user_func));
819            incomplete_class = 1;
820            ce = PHP_IC_ENTRY;
821        }
822
823        zval_ptr_dtor(&user_func);
824        zval_ptr_dtor(&args[0]);
825        break;
826    } while (1);
827
828    *p = YYCURSOR;
829
830    if (custom_object) {
831        int ret;
832
833        ret = object_custom(UNSERIALIZE_PASSTHRU, ce);
834
835        if (ret && incomplete_class) {
836            php_store_class_name(rval, class_name->val, len2);
837        }
838        zend_string_release(class_name);
839        return ret;
840    }
841
842    elements = object_common1(UNSERIALIZE_PASSTHRU, ce);
843
844    if (incomplete_class) {
845        php_store_class_name(rval, class_name->val, len2);
846    }
847    zend_string_release(class_name);
848
849    return object_common2(UNSERIALIZE_PASSTHRU, elements);
850}
851
852"}" {
853    /* this is the case where we have less data than planned */
854    php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data");
855    return 0; /* not sure if it should be 0 or 1 here? */
856}
857
858any { return 0; }
859
860*/
861
862    return 0;
863}
864