summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnatol Belski <ab@php.net>2014-04-17 10:48:14 +0200
committerAnatol Belski <ab@php.net>2014-04-17 10:48:14 +0200
commit5328d4289946e260232f3195ba2e0f0eb173d5ef (patch)
tree1b45041613881715d91bde6b9cab41b1f0e9bebc
parent7a5f1663c6775bbdaf870e4c71ef8813d5d13179 (diff)
downloadphp-git-5328d4289946e260232f3195ba2e0f0eb173d5ef.tar.gz
Fixed bug #67072 Echoing unserialized "SplFileObject" crash
The actual issue lays in the unserializer code which doesn't honor the unserialize callback. By contrast, the serialize callback is respected. This leads to the situation that even if a class has disabled the serialization explicitly, user could still construct a vulnerable string which would result bad things when trying to unserialize. This conserns also the classes implementing Serializable as well as some core classes disabling serialize/unserialize callbacks explicitly (PDO, SimpleXML, SplFileInfo and co). As of now, the flow is first to call the unserialize callback (if available), then call __wakeup. If the unserialize callback returns with no success, no object is instantiated. This makes the scheme used by internal classes effective, to disable unserialize just assign zend_class_unserialize_deny as callback.
-rw-r--r--NEWS4
-rw-r--r--ext/standard/tests/serialize/005.phpt1
-rw-r--r--ext/standard/tests/serialize/bug67072.phpt13
-rw-r--r--ext/standard/var_unserializer.c75
-rw-r--r--ext/standard/var_unserializer.re11
5 files changed, 70 insertions, 34 deletions
diff --git a/NEWS b/NEWS
index 4c2a97e722..2ba98b0b0f 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,10 @@ PHP NEWS
. Fixed bug #67081 (DOMDocumentType->internalSubset returns entire DOCTYPE tag,
not only the subset). (Anatol)
+- Standard:
+ . Fixed bug #67072 (Echoing unserialized "SplFileObject" crash). (Anatol)
+
+
?? ??? 2014, PHP 5.4.28
- Core:
diff --git a/ext/standard/tests/serialize/005.phpt b/ext/standard/tests/serialize/005.phpt
index e7b23db701..87a3fb3ab7 100644
--- a/ext/standard/tests/serialize/005.phpt
+++ b/ext/standard/tests/serialize/005.phpt
@@ -156,6 +156,7 @@ object(TestNAOld)#%d (0) {
}
===NANew===
unserializer(TestNANew)
+TestNew::unserialize()
TestNew::__wakeup()
object(TestNANew)#%d (0) {
}
diff --git a/ext/standard/tests/serialize/bug67072.phpt b/ext/standard/tests/serialize/bug67072.phpt
new file mode 100644
index 0000000000..3e7d33ba8a
--- /dev/null
+++ b/ext/standard/tests/serialize/bug67072.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Bug #67072 Echoing unserialized "SplFileObject" crash
+--FILE--
+<?php
+ echo unserialize('O:13:"SplFileObject":1:{s:9:"*filename";s:15:"/home/flag/flag";}');
+?>
+===DONE==
+--EXPECTF--
+Fatal error: Uncaught exception 'Exception' with message 'Unserialization of 'SplFileObject' is not allowed' in %sbug67072.php:2
+Stack trace:
+#0 %sbug67072.php(2): unserialize('O:13:"SplFileOb...')
+#1 {main}
+ thrown in %sbug67072.php on line 2
diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c
index e39a3505cd..8db00c239f 100644
--- a/ext/standard/var_unserializer.c
+++ b/ext/standard/var_unserializer.c
@@ -1,10 +1,10 @@
-/* Generated by re2c 0.13.5 on Mon Jul 29 17:57:26 2013 */
+/* Generated by re2c 0.13.5 on Thu Apr 17 10:03:26 2014 */
#line 1 "ext/standard/var_unserializer.re"
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
- | Copyright (c) 1997-2014 The PHP Group |
+ | Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
@@ -396,7 +396,12 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
(*p) += 2;
- object_init_ex(*rval, ce);
+ if (ce->unserialize == NULL) {
+ object_init_ex(*rval, ce);
+ } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, elements, (zend_unserialize_data *)var_hash TSRMLS_CC) != SUCCESS) {
+ return 0;
+ }
+
return elements;
}
@@ -408,6 +413,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements)
zval *retval_ptr = NULL;
zval fname;
+ if (Z_TYPE_PP(rval) != IS_OBJECT) {
+ return 0;
+ }
+
if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) {
return 0;
}
@@ -457,7 +466,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
-#line 461 "ext/standard/var_unserializer.c"
+#line 470 "ext/standard/var_unserializer.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -517,9 +526,9 @@ yy2:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy95;
yy3:
-#line 812 "ext/standard/var_unserializer.re"
+#line 821 "ext/standard/var_unserializer.re"
{ return 0; }
-#line 523 "ext/standard/var_unserializer.c"
+#line 532 "ext/standard/var_unserializer.c"
yy4:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy89;
@@ -562,13 +571,13 @@ yy13:
goto yy3;
yy14:
++YYCURSOR;
-#line 806 "ext/standard/var_unserializer.re"
+#line 815 "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 572 "ext/standard/var_unserializer.c"
+#line 581 "ext/standard/var_unserializer.c"
yy16:
yych = *++YYCURSOR;
goto yy3;
@@ -598,7 +607,7 @@ yy20:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 660 "ext/standard/var_unserializer.re"
+#line 669 "ext/standard/var_unserializer.re"
{
size_t len, len2, len3, maxlen;
long elements;
@@ -744,7 +753,7 @@ yy20:
return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
-#line 748 "ext/standard/var_unserializer.c"
+#line 757 "ext/standard/var_unserializer.c"
yy25:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -769,7 +778,7 @@ yy27:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 652 "ext/standard/var_unserializer.re"
+#line 661 "ext/standard/var_unserializer.re"
{
INIT_PZVAL(*rval);
@@ -777,7 +786,7 @@ yy27:
return object_common2(UNSERIALIZE_PASSTHRU,
object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
}
-#line 781 "ext/standard/var_unserializer.c"
+#line 790 "ext/standard/var_unserializer.c"
yy32:
yych = *++YYCURSOR;
if (yych == '+') goto yy33;
@@ -798,7 +807,7 @@ yy34:
yych = *++YYCURSOR;
if (yych != '{') goto yy18;
++YYCURSOR;
-#line 632 "ext/standard/var_unserializer.re"
+#line 641 "ext/standard/var_unserializer.re"
{
long elements = parse_iv(start + 2);
/* use iv() not uiv() in order to check data range */
@@ -818,7 +827,7 @@ yy34:
return finish_nested_data(UNSERIALIZE_PASSTHRU);
}
-#line 822 "ext/standard/var_unserializer.c"
+#line 831 "ext/standard/var_unserializer.c"
yy39:
yych = *++YYCURSOR;
if (yych == '+') goto yy40;
@@ -839,7 +848,7 @@ yy41:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 603 "ext/standard/var_unserializer.re"
+#line 612 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
char *str;
@@ -868,7 +877,7 @@ yy41:
ZVAL_STRINGL(*rval, str, len, 0);
return 1;
}
-#line 872 "ext/standard/var_unserializer.c"
+#line 881 "ext/standard/var_unserializer.c"
yy46:
yych = *++YYCURSOR;
if (yych == '+') goto yy47;
@@ -889,7 +898,7 @@ yy48:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 575 "ext/standard/var_unserializer.re"
+#line 584 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
char *str;
@@ -917,7 +926,7 @@ yy48:
ZVAL_STRINGL(*rval, str, len, 1);
return 1;
}
-#line 921 "ext/standard/var_unserializer.c"
+#line 930 "ext/standard/var_unserializer.c"
yy53:
yych = *++YYCURSOR;
if (yych <= '/') {
@@ -1005,7 +1014,7 @@ yy61:
}
yy63:
++YYCURSOR;
-#line 565 "ext/standard/var_unserializer.re"
+#line 574 "ext/standard/var_unserializer.re"
{
#if SIZEOF_LONG == 4
use_double:
@@ -1015,7 +1024,7 @@ use_double:
ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL));
return 1;
}
-#line 1019 "ext/standard/var_unserializer.c"
+#line 1028 "ext/standard/var_unserializer.c"
yy65:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1074,7 +1083,7 @@ yy73:
yych = *++YYCURSOR;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 550 "ext/standard/var_unserializer.re"
+#line 559 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
@@ -1089,7 +1098,7 @@ yy73:
return 1;
}
-#line 1093 "ext/standard/var_unserializer.c"
+#line 1102 "ext/standard/var_unserializer.c"
yy76:
yych = *++YYCURSOR;
if (yych == 'N') goto yy73;
@@ -1116,7 +1125,7 @@ yy79:
if (yych <= '9') goto yy79;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 523 "ext/standard/var_unserializer.re"
+#line 532 "ext/standard/var_unserializer.re"
{
#if SIZEOF_LONG == 4
int digits = YYCURSOR - start - 3;
@@ -1143,7 +1152,7 @@ yy79:
ZVAL_LONG(*rval, parse_iv(start + 2));
return 1;
}
-#line 1147 "ext/standard/var_unserializer.c"
+#line 1156 "ext/standard/var_unserializer.c"
yy83:
yych = *++YYCURSOR;
if (yych <= '/') goto yy18;
@@ -1151,24 +1160,24 @@ yy83:
yych = *++YYCURSOR;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 516 "ext/standard/var_unserializer.re"
+#line 525 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_BOOL(*rval, parse_iv(start + 2));
return 1;
}
-#line 1162 "ext/standard/var_unserializer.c"
+#line 1171 "ext/standard/var_unserializer.c"
yy87:
++YYCURSOR;
-#line 509 "ext/standard/var_unserializer.re"
+#line 518 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_NULL(*rval);
return 1;
}
-#line 1172 "ext/standard/var_unserializer.c"
+#line 1181 "ext/standard/var_unserializer.c"
yy89:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1191,7 +1200,7 @@ yy91:
if (yych <= '9') goto yy91;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 486 "ext/standard/var_unserializer.re"
+#line 495 "ext/standard/var_unserializer.re"
{
long id;
@@ -1214,7 +1223,7 @@ yy91:
return 1;
}
-#line 1218 "ext/standard/var_unserializer.c"
+#line 1227 "ext/standard/var_unserializer.c"
yy95:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1237,7 +1246,7 @@ yy97:
if (yych <= '9') goto yy97;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 465 "ext/standard/var_unserializer.re"
+#line 474 "ext/standard/var_unserializer.re"
{
long id;
@@ -1258,9 +1267,9 @@ yy97:
return 1;
}
-#line 1262 "ext/standard/var_unserializer.c"
+#line 1271 "ext/standard/var_unserializer.c"
}
-#line 814 "ext/standard/var_unserializer.re"
+#line 823 "ext/standard/var_unserializer.re"
return 0;
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index 76c501e1b5..1f71bb759d 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -400,7 +400,12 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
(*p) += 2;
- object_init_ex(*rval, ce);
+ if (ce->unserialize == NULL) {
+ object_init_ex(*rval, ce);
+ } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, elements, (zend_unserialize_data *)var_hash TSRMLS_CC) != SUCCESS) {
+ return 0;
+ }
+
return elements;
}
@@ -412,6 +417,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements)
zval *retval_ptr = NULL;
zval fname;
+ if (Z_TYPE_PP(rval) != IS_OBJECT) {
+ return 0;
+ }
+
if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) {
return 0;
}