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