- Add some checks when unserializing data to prevent buffer overflows

This commit is contained in:
Marcus Boerger 2004-09-05 16:29:05 +00:00
parent 945f7d68b9
commit 91af7f394f
4 changed files with 203 additions and 112 deletions

View File

@ -46,7 +46,7 @@ struct php_unserialize_data {
typedef struct php_unserialize_data php_unserialize_data_t;
PHPAPI void php_var_serialize(smart_str *buf, zval **struc, php_serialize_data_t *var_hash TSRMLS_DC);
PHPAPI int php_var_unserialize(zval **rval, const char **p, const char *max, php_unserialize_data_t *var_hash TSRMLS_DC);
PHPAPI int php_var_unserialize(zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC);
#define PHP_VAR_SERIALIZE_INIT(var_hash) \
zend_hash_init(&(var_hash), 10, NULL, NULL, 0)

View File

@ -3,7 +3,57 @@ Bug #25378 (unserialize() crashes with invalid data)
--FILE--
<?php
var_dump(unserialize("s:-1:\"\";"));
var_dump(unserialize("i:823"));
var_dump(unserialize("O:8:\"stdClass :0:{}"));
var_dump(unserialize("O:8:\"stdClass\"+0:{}"));
var_dump(unserialize("O:1000:\"stdClass\":0:{}"));
var_dump(unserialize("a:2:{i:0;s:2:\"12\":"));
var_dump(unserialize("a:2:{i:0;s:2:\"12\";i:1;s:3000:\"123"));
var_dump(unserialize("a:2:{i:0;s:2:\"12\"+i:1;s:3:\"123\";}"));
var_dump(unserialize("a:2:{i:0;s:2:\"12\";i:1;s:3:\"123\";"));
var_dump(unserialize("s:3000:\"123\";"));
var_dump(unserialize("s:3000:\"123"));
var_dump(unserialize("s:3:\"123;"));
var_dump(unserialize("s:0:\"123\";"));
?>
===DONE===
--EXPECTF--
Notice: unserialize(): Error at offset 0 of 8 bytes in %s on line %d
Notice: unserialize(): Error at offset 0 of 8 bytes in %sbug25378.php on line %d
bool(false)
Notice: unserialize(): Error at offset 0 of 5 bytes in %sbug25378.php on line %d
bool(false)
Notice: unserialize(): Error at offset 13 of 19 bytes in %sbug25378.php on line %d
bool(false)
Notice: unserialize(): Error at offset 14 of 19 bytes in %sbug25378.php on line %d
bool(false)
Notice: unserialize(): Error at offset 2 of 22 bytes in %sbug25378.php on line %d
bool(false)
Notice: unserialize(): Error at offset 17 of 18 bytes in %sbug25378.php on line %d
bool(false)
Notice: unserialize(): Error at offset 24 of 33 bytes in %sbug25378.php on line %d
bool(false)
Notice: unserialize(): Error at offset 17 of 33 bytes in %sbug25378.php on line %d
bool(false)
Notice: unserialize(): Error at offset 33 of 32 bytes in %sbug25378.php on line %d
bool(false)
Notice: unserialize(): Error at offset 2 of 13 bytes in %sbug25378.php on line %d
bool(false)
Notice: unserialize(): Error at offset 2 of 11 bytes in %sbug25378.php on line %d
bool(false)
Notice: unserialize(): Error at offset 8 of 9 bytes in %sbug25378.php on line %d
bool(false)
Notice: unserialize(): Error at offset 5 of 10 bytes in %sbug25378.php on line %d
bool(false)
===DONE===

View File

@ -823,7 +823,7 @@ PHP_FUNCTION(unserialize)
}
if (Z_TYPE_PP(buf) == IS_STRING) {
const char *p = Z_STRVAL_PP(buf);
const unsigned char *p = (unsigned char*)Z_STRVAL_PP(buf);
if (Z_STRLEN_PP(buf) == 0) {
RETURN_FALSE;
@ -833,7 +833,7 @@ PHP_FUNCTION(unserialize)
if (!php_var_unserialize(&return_value, &p, p + Z_STRLEN_PP(buf), &var_hash TSRMLS_CC)) {
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
zval_dtor(return_value);
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)(p - Z_STRVAL_PP(buf)), Z_STRLEN_PP(buf));
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - Z_STRVAL_PP(buf)), Z_STRLEN_PP(buf));
RETURN_FALSE;
}
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);

View File

@ -1,5 +1,5 @@
/* Generated by re2c 0.9.2 on Sat Mar 27 02:27:57 2004 */
#line 1 "/usr/src/php5/ext/standard/var_unserializer.re"
/* Generated by re2c 0.9.4 on Thu Sep 2 20:41:03 2004 */
#line 1 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
@ -111,12 +111,12 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
#define YYMARKER marker
#line 118 "/usr/src/php5/ext/standard/var_unserializer.re"
#line 118 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
static inline int parse_iv2(const char *p, const char **q)
static inline int parse_iv2(const unsigned char *p, const unsigned char **q)
{
char cursor;
int result = 0;
@ -131,7 +131,7 @@ static inline int parse_iv2(const char *p, const char **q)
}
while (1) {
cursor = *p;
cursor = (char)*p;
if (cursor >= '0' && cursor <= '9') {
result = result * 10 + cursor - '0';
} else {
@ -144,12 +144,34 @@ static inline int parse_iv2(const char *p, const char **q)
return result;
}
static inline int parse_iv(const char *p)
static inline int parse_iv(const unsigned char *p)
{
return parse_iv2(p, NULL);
}
#define UNSERIALIZE_PARAMETER zval **rval, const char **p, const char *max, php_unserialize_data_t *var_hash TSRMLS_DC
/* no need to check for length - re2c already did */
static inline size_t parse_uiv(const unsigned char *p)
{
unsigned char cursor;
size_t result = 0;
if (*p == '+') {
p++;
}
while (1) {
cursor = *p;
if (cursor >= '0' && cursor <= '9') {
result = result * 10 + (size_t)(cursor - (unsigned char)'0');
} else {
break;
}
p++;
}
return result;
}
#define UNSERIALIZE_PARAMETER zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC
#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash TSRMLS_CC
static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, int elements)
@ -187,6 +209,14 @@ static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, int
zval_dtor(key);
FREE_ZVAL(key);
if (elements && *(*p-1) != ';') {
#if SOMETHING_NEW_MIGHT_LEAD_TO_CRASH_ENABLE_IF_YOU_ARE_BRAVE
zval_ptr_dtor(rval);
#endif
(*p)--;
return 0;
}
}
return 1;
@ -253,7 +283,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
#line 7 "re2c-output.c"
#line 7 "<stdout>"
{
YYCTYPE yych;
unsigned int yyaccept;
@ -332,7 +362,7 @@ yy0:
goto yy16;
} else {
if(yych <= '}') goto yy14;
if(yych <= '\277') goto yy16;
if(yych <= 0xBF) goto yy16;
goto yy2;
}
}
@ -343,18 +373,18 @@ yy2: YYCURSOR = YYMARKER;
}
yy3: yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if(yych == ':') goto yy89;
if(yych == ':') goto yy87;
goto yy4;
yy4:
#line 478 "/usr/src/php5/ext/standard/var_unserializer.re"
#line 530 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
{ return 0; }
#line 102 "re2c-output.c"
#line 102 "<stdout>"
yy5: yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if(yych == ':') goto yy83;
if(yych == ':') goto yy81;
goto yy4;
yy6: yych = *++YYCURSOR;
if(yych == ';') goto yy81;
if(yych == ';') goto yy79;
goto yy4;
yy7: yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
@ -384,16 +414,16 @@ yy13: yyaccept = 0;
yych = *(YYMARKER = ++YYCURSOR);
if(yych == ':') goto yy17;
goto yy4;
yy14: yych = *++YYCURSOR;
yy14: ++YYCURSOR;
goto yy15;
yy15:
#line 472 "/usr/src/php5/ext/standard/var_unserializer.re"
#line 524 "/usr/src/php-cvs/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 147 "re2c-output.c"
#line 147 "<stdout>"
yy16: yych = *++YYCURSOR;
goto yy4;
yy17: yych = *++YYCURSOR;
@ -413,14 +443,13 @@ yy20: if(yybm[0+yych] & 128) goto yy19;
yy21: yych = *++YYCURSOR;
if(yych != '"') goto yy2;
goto yy22;
yy22: yych = *++YYCURSOR;
yy22: ++YYCURSOR;
goto yy23;
yy23:
#line 393 "/usr/src/php5/ext/standard/var_unserializer.re"
#line 431 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
{
int len;
size_t len, len2, maxlen;
int elements;
int len2;
char *class_name;
zend_class_entry *ce;
zend_class_entry **pce;
@ -432,13 +461,28 @@ yy23:
zval *arg_func_name;
INIT_PZVAL(*rval);
len2 = len = parse_iv(start + 2);
if (len == 0)
len2 = len = parse_uiv(start + 2);
maxlen = max - YYCURSOR;
if (maxlen < len || len == 0) {
*p = start + 2;
return 0;
}
class_name = (char*)YYCURSOR;
class_name = estrndup(YYCURSOR, len);
YYCURSOR += len;
if (*(YYCURSOR) != '"') {
*p = YYCURSOR;
return 0;
}
if (*(YYCURSOR+1) != ':') {
*p = YYCURSOR+1;
return 0;
}
class_name = estrndup(class_name, len);
do {
/* Try to find class directly */
if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) {
@ -495,7 +539,7 @@ yy23:
return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
#line 249 "re2c-output.c"
#line 264 "<stdout>"
yy24: yych = *++YYCURSOR;
if(yych <= ','){
if(yych != '+') goto yy2;
@ -521,10 +565,10 @@ yy27: if(yych <= '/') goto yy2;
yy28: yych = *++YYCURSOR;
if(yych != '"') goto yy2;
goto yy29;
yy29: yych = *++YYCURSOR;
yy29: ++YYCURSOR;
goto yy30;
yy30:
#line 385 "/usr/src/php5/ext/standard/var_unserializer.re"
#line 423 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
{
INIT_PZVAL(*rval);
@ -532,7 +576,7 @@ yy30:
return object_common2(UNSERIALIZE_PASSTHRU,
object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
}
#line 286 "re2c-output.c"
#line 302 "<stdout>"
yy31: yych = *++YYCURSOR;
if(yych == '+') goto yy32;
if(yych <= '/') goto yy2;
@ -553,10 +597,10 @@ yy34: if(yych <= '/') goto yy2;
yy35: yych = *++YYCURSOR;
if(yych != '{') goto yy2;
goto yy36;
yy36: yych = *++YYCURSOR;
yy36: ++YYCURSOR;
goto yy37;
yy37:
#line 367 "/usr/src/php5/ext/standard/var_unserializer.re"
#line 405 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
{
int elements = parse_iv(start + 2);
@ -574,7 +618,7 @@ yy37:
return finish_nested_data(UNSERIALIZE_PASSTHRU);
}
#line 328 "re2c-output.c"
#line 345 "<stdout>"
yy38: yych = *++YYCURSOR;
if(yych == '+') goto yy39;
if(yych <= '/') goto yy2;
@ -595,26 +639,38 @@ yy41: if(yych <= '/') goto yy2;
yy42: yych = *++YYCURSOR;
if(yych != '"') goto yy2;
goto yy43;
yy43: yych = *++YYCURSOR;
yy43: ++YYCURSOR;
goto yy44;
yy44:
#line 347 "/usr/src/php5/ext/standard/var_unserializer.re"
#line 377 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
{
int len;
size_t len, maxlen;
char *str;
len = parse_iv(start + 2);
len = parse_uiv(start + 2);
maxlen = max - YYCURSOR;
if (maxlen < len) {
*p = start + 2;
return 0;
}
str = estrndup(YYCURSOR, len);
str = (char*)YYCURSOR;
YYCURSOR += len + 2;
YYCURSOR += len;
if (*(YYCURSOR) != '"') {
*p = YYCURSOR;
return 0;
}
YYCURSOR += 2;
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_STRINGL(*rval, str, len, 0);
ZVAL_STRINGL(*rval, str, len, 1);
return 1;
}
#line 372 "re2c-output.c"
#line 398 "<stdout>"
yy45: yych = *++YYCURSOR;
if(yych <= '/'){
if(yych <= ','){
@ -700,17 +756,17 @@ yy54: if(yych <= ';'){
goto yy2;
}
}
yy55: yych = *++YYCURSOR;
yy55: ++YYCURSOR;
goto yy56;
yy56:
#line 340 "/usr/src/php5/ext/standard/var_unserializer.re"
#line 370 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_DOUBLE(*rval, atof(start + 2));
return 1;
}
#line 468 "re2c-output.c"
#line 496 "<stdout>"
yy57: yych = *++YYCURSOR;
if(yych <= ','){
if(yych != '+') goto yy2;
@ -767,10 +823,10 @@ yy64: yych = *++YYCURSOR;
yy65: yych = *++YYCURSOR;
if(yych != ';') goto yy2;
goto yy66;
yy66: yych = *++YYCURSOR;
yy66: ++YYCURSOR;
goto yy67;
yy67:
#line 323 "/usr/src/php5/ext/standard/var_unserializer.re"
#line 353 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
@ -787,7 +843,7 @@ yy67:
#endif
return 1;
}
#line 545 "re2c-output.c"
#line 575 "<stdout>"
yy68: yych = *++YYCURSOR;
if(yych == 'N') goto yy65;
goto yy2;
@ -813,87 +869,72 @@ yy72: if(yych <= '/') goto yy2;
if(yych <= '9') goto yy71;
if(yych != ';') goto yy2;
goto yy73;
yy73: yych = *++YYCURSOR;
yy73: ++YYCURSOR;
goto yy74;
yy74:
#line 316 "/usr/src/php5/ext/standard/var_unserializer.re"
#line 346 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_LONG(*rval, parse_iv(start + 2));
return 1;
}
#line 581 "re2c-output.c"
#line 612 "<stdout>"
yy75: yych = *++YYCURSOR;
if(yych <= ','){
if(yych != '+') goto yy2;
goto yy76;
} else {
if(yych <= '-') goto yy76;
if(yych <= '/') goto yy2;
if(yych <= '9') goto yy77;
goto yy2;
}
yy76: yych = *++YYCURSOR;
if(yych <= '/') goto yy2;
if(yych >= ':') goto yy2;
if(yych >= '2') goto yy2;
goto yy76;
yy76: yych = *++YYCURSOR;
if(yych != ';') goto yy2;
goto yy77;
yy77: ++YYCURSOR;
if(YYLIMIT == YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
goto yy78;
yy78: if(yych <= '/') goto yy2;
if(yych <= '9') goto yy77;
if(yych != ';') goto yy2;
goto yy79;
yy79: yych = *++YYCURSOR;
goto yy80;
yy80:
#line 309 "/usr/src/php5/ext/standard/var_unserializer.re"
yy78:
#line 339 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_BOOL(*rval, parse_iv(start + 2));
return 1;
}
#line 614 "re2c-output.c"
yy81: yych = *++YYCURSOR;
goto yy82;
yy82:
#line 302 "/usr/src/php5/ext/standard/var_unserializer.re"
#line 630 "<stdout>"
yy79: ++YYCURSOR;
goto yy80;
yy80:
#line 332 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_NULL(*rval);
return 1;
}
#line 625 "re2c-output.c"
yy83: yych = *++YYCURSOR;
#line 641 "<stdout>"
yy81: yych = *++YYCURSOR;
if(yych <= ','){
if(yych != '+') goto yy2;
goto yy84;
goto yy82;
} else {
if(yych <= '-') goto yy84;
if(yych <= '-') goto yy82;
if(yych <= '/') goto yy2;
if(yych <= '9') goto yy85;
if(yych <= '9') goto yy83;
goto yy2;
}
yy84: yych = *++YYCURSOR;
yy82: yych = *++YYCURSOR;
if(yych <= '/') goto yy2;
if(yych >= ':') goto yy2;
goto yy85;
yy85: ++YYCURSOR;
goto yy83;
yy83: ++YYCURSOR;
if(YYLIMIT == YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
goto yy86;
yy86: if(yych <= '/') goto yy2;
if(yych <= '9') goto yy85;
goto yy84;
yy84: if(yych <= '/') goto yy2;
if(yych <= '9') goto yy83;
if(yych != ';') goto yy2;
goto yy87;
yy87: yych = *++YYCURSOR;
goto yy88;
yy88:
#line 281 "/usr/src/php5/ext/standard/var_unserializer.re"
goto yy85;
yy85: ++YYCURSOR;
goto yy86;
yy86:
#line 311 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
{
int id;
@ -914,33 +955,33 @@ yy88:
return 1;
}
#line 672 "re2c-output.c"
yy89: yych = *++YYCURSOR;
#line 689 "<stdout>"
yy87: yych = *++YYCURSOR;
if(yych <= ','){
if(yych != '+') goto yy2;
goto yy90;
goto yy88;
} else {
if(yych <= '-') goto yy90;
if(yych <= '-') goto yy88;
if(yych <= '/') goto yy2;
if(yych <= '9') goto yy91;
if(yych <= '9') goto yy89;
goto yy2;
}
yy90: yych = *++YYCURSOR;
yy88: yych = *++YYCURSOR;
if(yych <= '/') goto yy2;
if(yych >= ':') goto yy2;
goto yy91;
yy91: ++YYCURSOR;
goto yy89;
yy89: ++YYCURSOR;
if(YYLIMIT == YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
goto yy92;
yy92: if(yych <= '/') goto yy2;
if(yych <= '9') goto yy91;
goto yy90;
yy90: if(yych <= '/') goto yy2;
if(yych <= '9') goto yy89;
if(yych != ';') goto yy2;
goto yy93;
yy93: yych = *++YYCURSOR;
goto yy94;
yy94:
#line 260 "/usr/src/php5/ext/standard/var_unserializer.re"
goto yy91;
yy91: ++YYCURSOR;
goto yy92;
yy92:
#line 290 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
{
int id;
@ -961,9 +1002,9 @@ yy94:
return 1;
}
#line 719 "re2c-output.c"
#line 737 "<stdout>"
}
#line 480 "/usr/src/php5/ext/standard/var_unserializer.re"
#line 532 "/usr/src/php-cvs/ext/standard/var_unserializer.re"
return 0;