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