From 448c9be157f4147e121f1a2a524536c75c9c6059 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Tue, 2 Aug 2016 01:08:42 -0700 Subject: [PATCH] Fix bug #72663 - destroy broken object when unserializing --- ext/standard/tests/strings/bug72663.phpt | 26 ++++++++ ext/standard/tests/strings/bug72663_2.phpt | 17 ++++++ ext/standard/var_unserializer.c | 70 ++++++++++++---------- ext/standard/var_unserializer.re | 5 +- 4 files changed, 84 insertions(+), 34 deletions(-) create mode 100644 ext/standard/tests/strings/bug72663.phpt create mode 100644 ext/standard/tests/strings/bug72663_2.phpt diff --git a/ext/standard/tests/strings/bug72663.phpt b/ext/standard/tests/strings/bug72663.phpt new file mode 100644 index 0000000000..e61f939d4d --- /dev/null +++ b/ext/standard/tests/strings/bug72663.phpt @@ -0,0 +1,26 @@ +--TEST-- +Bug #72663: Create an Unexpected Object and Don't Invoke __wakeup() in Deserialization +--FILE-- +data); + } + function unserialize($data) { + $this->data = unserialize($data); + } +} + +$inner = 'a:1:{i:0;O:9:"Exception":2:{s:7:"'."\0".'*'."\0".'file";R:4;}'; +$exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:4;}'; + +$data = unserialize($exploit); +echo $data[1]; +?> +DONE +--EXPECTF-- +Notice: unserialize(): Unexpected end of serialized data in %sbug72663.php on line %d + +Notice: unserialize(): Error at offset 46 of 47 bytes in %sbug72663.php on line %d +DONE \ No newline at end of file diff --git a/ext/standard/tests/strings/bug72663_2.phpt b/ext/standard/tests/strings/bug72663_2.phpt new file mode 100644 index 0000000000..ac605e9fd2 --- /dev/null +++ b/ext/standard/tests/strings/bug72663_2.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #72663: Create an Unexpected Object and Don't Invoke __wakeup() in Deserialization +--FILE-- + +DONE +--EXPECTF-- +Notice: session_decode(): Unexpected end of serialized data in %sbug72663_2.php on line %d +array(0) { +} +DONE \ No newline at end of file diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index 1e45b03fcc..e4ddecff66 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 */ +/* Generated by re2c 0.13.7.5 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -408,7 +408,7 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) elements = parse_iv2((*p) + 2, p); (*p) += 2; - + if (ce->serialize == NULL) { object_init_ex(*rval, ce); } else { @@ -434,6 +434,9 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) } if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) { + /* We've got partially constructed object on our hands here. Wipe it */ + zend_hash_clean(Z_OBJPROP_PP(rval)); + ZVAL_NULL(*rval); return 0; } @@ -482,7 +485,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) -#line 486 "ext/standard/var_unserializer.c" +#line 489 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -542,9 +545,9 @@ yy2: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 851 "ext/standard/var_unserializer.re" +#line 854 "ext/standard/var_unserializer.re" { return 0; } -#line 548 "ext/standard/var_unserializer.c" +#line 551 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -587,13 +590,13 @@ yy13: goto yy3; yy14: ++YYCURSOR; -#line 845 "ext/standard/var_unserializer.re" +#line 848 "ext/standard/var_unserializer.re" { /* this is the case where we have less data than planned */ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data"); return 0; /* not sure if it should be 0 or 1 here? */ } -#line 597 "ext/standard/var_unserializer.c" +#line 600 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -619,11 +622,12 @@ yy20: if (yybm[0+yych] & 128) { goto yy20; } - if (yych != ':') goto yy18; + if (yych <= '/') goto yy18; + if (yych >= ';') goto yy18; yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 698 "ext/standard/var_unserializer.re" +#line 701 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; long elements; @@ -770,7 +774,7 @@ yy20: return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 774 "ext/standard/var_unserializer.c" +#line 778 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -795,7 +799,7 @@ yy27: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 689 "ext/standard/var_unserializer.re" +#line 692 "ext/standard/var_unserializer.re" { if (!var_hash) return 0; @@ -804,7 +808,7 @@ yy27: return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } -#line 808 "ext/standard/var_unserializer.c" +#line 812 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -825,7 +829,7 @@ yy34: yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 668 "ext/standard/var_unserializer.re" +#line 671 "ext/standard/var_unserializer.re" { long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@ -846,7 +850,7 @@ yy34: return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 850 "ext/standard/var_unserializer.c" +#line 854 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -867,7 +871,7 @@ yy41: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 633 "ext/standard/var_unserializer.re" +#line 636 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -902,7 +906,7 @@ yy41: ZVAL_STRINGL(*rval, str, len, 0); return 1; } -#line 906 "ext/standard/var_unserializer.c" +#line 910 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -923,7 +927,7 @@ yy48: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 600 "ext/standard/var_unserializer.re" +#line 603 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -956,7 +960,7 @@ yy48: ZVAL_STRINGL(*rval, str, len, 1); return 1; } -#line 960 "ext/standard/var_unserializer.c" +#line 964 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -1044,7 +1048,7 @@ yy61: } yy63: ++YYCURSOR; -#line 590 "ext/standard/var_unserializer.re" +#line 593 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 use_double: @@ -1054,7 +1058,7 @@ use_double: ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); return 1; } -#line 1058 "ext/standard/var_unserializer.c" +#line 1062 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1113,7 +1117,7 @@ yy73: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 575 "ext/standard/var_unserializer.re" +#line 578 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); @@ -1128,7 +1132,7 @@ yy73: return 1; } -#line 1132 "ext/standard/var_unserializer.c" +#line 1136 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1155,7 +1159,7 @@ yy79: if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; -#line 548 "ext/standard/var_unserializer.re" +#line 551 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1182,7 +1186,7 @@ yy79: ZVAL_LONG(*rval, parse_iv(start + 2)); return 1; } -#line 1186 "ext/standard/var_unserializer.c" +#line 1190 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1190,24 +1194,24 @@ yy83: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 541 "ext/standard/var_unserializer.re" +#line 544 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); return 1; } -#line 1201 "ext/standard/var_unserializer.c" +#line 1205 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 534 "ext/standard/var_unserializer.re" +#line 537 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); return 1; } -#line 1211 "ext/standard/var_unserializer.c" +#line 1215 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1230,7 +1234,7 @@ yy91: if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; -#line 511 "ext/standard/var_unserializer.re" +#line 514 "ext/standard/var_unserializer.re" { long id; @@ -1253,7 +1257,7 @@ yy91: return 1; } -#line 1257 "ext/standard/var_unserializer.c" +#line 1261 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1276,7 +1280,7 @@ yy97: if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; -#line 490 "ext/standard/var_unserializer.re" +#line 493 "ext/standard/var_unserializer.re" { long id; @@ -1297,9 +1301,9 @@ yy97: return 1; } -#line 1301 "ext/standard/var_unserializer.c" +#line 1305 "ext/standard/var_unserializer.c" } -#line 853 "ext/standard/var_unserializer.re" +#line 856 "ext/standard/var_unserializer.re" return 0; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index d1d4ef98e4..c1c18c9391 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -412,7 +412,7 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) elements = parse_iv2((*p) + 2, p); (*p) += 2; - + if (ce->serialize == NULL) { object_init_ex(*rval, ce); } else { @@ -438,6 +438,9 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) } if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) { + /* We've got partially constructed object on our hands here. Wipe it. */ + zend_hash_clean(Z_OBJPROP_PP(rval)); + ZVAL_NULL(*rval); return 0; } -- 2.40.0