From: Marcus Boerger Date: Sun, 5 Sep 2004 16:29:05 +0000 (+0000) Subject: - Add some checks when unserializing data to prevent buffer overflows X-Git-Tag: PRE_ZEND_VM_DISPATCH_PATCH~10 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=91af7f394f2c62e9f452b3a24f1f5ecfc141be8d;p=php - Add some checks when unserializing data to prevent buffer overflows --- diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h index 52c4f65f4d..c17929afa6 100644 --- a/ext/standard/php_var.h +++ b/ext/standard/php_var.h @@ -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) diff --git a/ext/standard/tests/serialize/bug25378.phpt b/ext/standard/tests/serialize/bug25378.phpt index e59044002f..e865b96e99 100644 --- a/ext/standard/tests/serialize/bug25378.phpt +++ b/ext/standard/tests/serialize/bug25378.phpt @@ -3,7 +3,57 @@ Bug #25378 (unserialize() crashes with invalid data) --FILE-- +===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=== diff --git a/ext/standard/var.c b/ext/standard/var.c index 81c80cba56..3d18d753e5 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -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); diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index 649661bcae..5e01096f44 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -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 "" { 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 "" 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 "" 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 "" 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 "" 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 "" 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 = (char*)YYCURSOR; + + YYCURSOR += len; - str = estrndup(YYCURSOR, len); + if (*(YYCURSOR) != '"') { + *p = YYCURSOR; + return 0; + } - YYCURSOR += len + 2; + 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 "" 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 "" 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 "" 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 "" 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 "" +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 "" +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 "" +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 "" } -#line 480 "/usr/src/php5/ext/standard/var_unserializer.re" +#line 532 "/usr/src/php-cvs/ext/standard/var_unserializer.re" return 0;