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