Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(81)

Side by Side Diff: base/json/json_parser.cc

Issue 2859513002: Fix potential buffer over-read errors for un-terminated JSON strings and comments. (Closed)
Patch Set: Address comments Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | base/json/json_parser_unittest.cc » ('j') | base/json/json_parser_unittest.cc » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/json/json_parser.h" 5 #include "base/json/json_parser.h"
6 6
7 #include <cmath> 7 #include <cmath>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/logging.h" 10 #include "base/logging.h"
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 default: 272 default:
273 return; 273 return;
274 } 274 }
275 } 275 }
276 } 276 }
277 277
278 bool JSONParser::EatComment() { 278 bool JSONParser::EatComment() {
279 if (*pos_ != '/' || !CanConsume(1)) 279 if (*pos_ != '/' || !CanConsume(1))
280 return false; 280 return false;
281 281
282 char next_char = *NextChar(); 282 NextChar();
brettw 2017/05/09 19:53:14 Did you consider making NextChar() return void? It
Robert Sesek 2017/05/09 21:27:36 Yeah, I was thinking about this because result of
283 if (next_char == '/') { 283
284 if (!CanConsume(1))
285 return false;
286
287 if (*pos_ == '/') {
284 // Single line comment, read to newline. 288 // Single line comment, read to newline.
285 while (CanConsume(1)) { 289 while (CanConsume(1)) {
286 next_char = *NextChar(); 290 if (*pos_ == '\n' || *pos_ == '\r')
287 if (next_char == '\n' || next_char == '\r')
288 return true; 291 return true;
292 NextChar();
289 } 293 }
290 } else if (next_char == '*') { 294 } else if (*pos_ == '*') {
291 char previous_char = '\0'; 295 char previous_char = '\0';
292 // Block comment, read until end marker. 296 // Block comment, read until end marker.
293 while (CanConsume(1)) { 297 while (CanConsume(1)) {
294 next_char = *NextChar(); 298 if (previous_char == '*' && *pos_ == '/') {
295 if (previous_char == '*' && next_char == '/') {
296 // EatWhitespaceAndComments will inspect pos_, which will still be on 299 // EatWhitespaceAndComments will inspect pos_, which will still be on
297 // the last / of the comment, so advance once more (which may also be 300 // the last / of the comment, so advance once more (which may also be
298 // end of input). 301 // end of input).
299 NextChar(); 302 NextChar();
300 return true; 303 return true;
301 } 304 }
302 previous_char = next_char; 305 previous_char = *pos_;
306 NextChar();
303 } 307 }
304 308
305 // If the comment is unterminated, GetNextToken will report T_END_OF_INPUT. 309 // If the comment is unterminated, GetNextToken will report T_END_OF_INPUT.
306 } 310 }
307 311
308 return false; 312 return false;
309 } 313 }
310 314
311 std::unique_ptr<Value> JSONParser::ParseNextToken() { 315 std::unique_ptr<Value> JSONParser::ParseNextToken() {
312 return ParseToken(GetNextToken()); 316 return ParseToken(GetNextToken());
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
447 451
448 return base::MakeUnique<Value>(string.DestructiveAsString()); 452 return base::MakeUnique<Value>(string.DestructiveAsString());
449 } 453 }
450 454
451 bool JSONParser::ConsumeStringRaw(StringBuilder* out) { 455 bool JSONParser::ConsumeStringRaw(StringBuilder* out) {
452 if (*pos_ != '"') { 456 if (*pos_ != '"') {
453 ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); 457 ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
454 return false; 458 return false;
455 } 459 }
456 460
461 // Strings are at minimum two characters: the surrounding double quotes.
462 if (!CanConsume(2)) {
463 ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
464 return false;
465 }
466
457 // StringBuilder will internally build a StringPiece unless a UTF-16 467 // StringBuilder will internally build a StringPiece unless a UTF-16
458 // conversion occurs, at which point it will perform a copy into a 468 // conversion occurs, at which point it will perform a copy into a
459 // std::string. 469 // std::string.
460 StringBuilder string(NextChar()); 470 StringBuilder string(NextChar());
461 471
472 // Handle the empty string case early.
473 if (*pos_ == '"') {
474 *out = std::move(string);
475 return true;
476 }
477
462 int length = end_pos_ - start_pos_; 478 int length = end_pos_ - start_pos_;
463 int32_t next_char = 0; 479 int32_t next_char = 0;
464 480
465 while (CanConsume(1)) { 481 // There must always be at least two characters left in the stream: the next
482 // string character and the terminating closing quote.
483 while (CanConsume(2)) {
466 int start_index = index_; 484 int start_index = index_;
467 pos_ = start_pos_ + index_; // CBU8_NEXT is postcrement. 485 pos_ = start_pos_ + index_; // CBU8_NEXT is postcrement.
468 CBU8_NEXT(start_pos_, index_, length, next_char); 486 CBU8_NEXT(start_pos_, index_, length, next_char);
469 if (next_char < 0 || !IsValidCharacter(next_char)) { 487 if (next_char < 0 || !IsValidCharacter(next_char)) {
470 if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) { 488 if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) {
471 ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1); 489 ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1);
472 return false; 490 return false;
473 } 491 }
474 CBU8_NEXT(start_pos_, start_index, length, next_char); 492 CBU8_NEXT(start_pos_, start_index, length, next_char);
475 string.Convert(); 493 string.Convert();
(...skipping 19 matching lines...) Expand all
495 // (either by combining the two characters of an encoded escape sequence, 513 // (either by combining the two characters of an encoded escape sequence,
496 // or with a UTF conversion), so using StringPiece isn't possible -- force 514 // or with a UTF conversion), so using StringPiece isn't possible -- force
497 // a conversion. 515 // a conversion.
498 string.Convert(); 516 string.Convert();
499 517
500 if (!CanConsume(1)) { 518 if (!CanConsume(1)) {
501 ReportError(JSONReader::JSON_INVALID_ESCAPE, 0); 519 ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
502 return false; 520 return false;
503 } 521 }
504 522
505 switch (*NextChar()) { 523 NextChar();
524 if (!CanConsume(1)) {
525 ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
526 return false;
527 }
528
529 switch (*pos_) {
506 // Allowed esape sequences: 530 // Allowed esape sequences:
507 case 'x': { // UTF-8 sequence. 531 case 'x': { // UTF-8 sequence.
508 // UTF-8 \x escape sequences are not allowed in the spec, but they 532 // UTF-8 \x escape sequences are not allowed in the spec, but they
509 // are supported here for backwards-compatiblity with the old parser. 533 // are supported here for backwards-compatiblity with the old parser.
510 if (!CanConsume(2)) { 534 if (!CanConsume(3)) {
511 ReportError(JSONReader::JSON_INVALID_ESCAPE, 1); 535 ReportError(JSONReader::JSON_INVALID_ESCAPE, 1);
512 return false; 536 return false;
513 } 537 }
514 538
515 int hex_digit = 0; 539 int hex_digit = 0;
516 if (!HexStringToInt(StringPiece(NextChar(), 2), &hex_digit) || 540 if (!HexStringToInt(StringPiece(NextChar(), 2), &hex_digit) ||
517 !IsValidCharacter(hex_digit)) { 541 !IsValidCharacter(hex_digit)) {
518 ReportError(JSONReader::JSON_INVALID_ESCAPE, -1); 542 ReportError(JSONReader::JSON_INVALID_ESCAPE, -1);
519 return false; 543 return false;
520 } 544 }
(...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after
770 return false; 794 return false;
771 795
772 return true; 796 return true;
773 } 797 }
774 798
775 std::unique_ptr<Value> JSONParser::ConsumeLiteral() { 799 std::unique_ptr<Value> JSONParser::ConsumeLiteral() {
776 switch (*pos_) { 800 switch (*pos_) {
777 case 't': { 801 case 't': {
778 const char kTrueLiteral[] = "true"; 802 const char kTrueLiteral[] = "true";
779 const int kTrueLen = static_cast<int>(strlen(kTrueLiteral)); 803 const int kTrueLen = static_cast<int>(strlen(kTrueLiteral));
780 if (!CanConsume(kTrueLen - 1) || 804 if (!CanConsume(kTrueLen) ||
781 !StringsAreEqual(pos_, kTrueLiteral, kTrueLen)) { 805 !StringsAreEqual(pos_, kTrueLiteral, kTrueLen)) {
782 ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); 806 ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
783 return nullptr; 807 return nullptr;
784 } 808 }
785 NextNChars(kTrueLen - 1); 809 NextNChars(kTrueLen - 1);
786 return base::MakeUnique<Value>(true); 810 return base::MakeUnique<Value>(true);
787 } 811 }
788 case 'f': { 812 case 'f': {
789 const char kFalseLiteral[] = "false"; 813 const char kFalseLiteral[] = "false";
790 const int kFalseLen = static_cast<int>(strlen(kFalseLiteral)); 814 const int kFalseLen = static_cast<int>(strlen(kFalseLiteral));
791 if (!CanConsume(kFalseLen - 1) || 815 if (!CanConsume(kFalseLen) ||
792 !StringsAreEqual(pos_, kFalseLiteral, kFalseLen)) { 816 !StringsAreEqual(pos_, kFalseLiteral, kFalseLen)) {
793 ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); 817 ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
794 return nullptr; 818 return nullptr;
795 } 819 }
796 NextNChars(kFalseLen - 1); 820 NextNChars(kFalseLen - 1);
797 return base::MakeUnique<Value>(false); 821 return base::MakeUnique<Value>(false);
798 } 822 }
799 case 'n': { 823 case 'n': {
800 const char kNullLiteral[] = "null"; 824 const char kNullLiteral[] = "null";
801 const int kNullLen = static_cast<int>(strlen(kNullLiteral)); 825 const int kNullLen = static_cast<int>(strlen(kNullLiteral));
802 if (!CanConsume(kNullLen - 1) || 826 if (!CanConsume(kNullLen) ||
803 !StringsAreEqual(pos_, kNullLiteral, kNullLen)) { 827 !StringsAreEqual(pos_, kNullLiteral, kNullLen)) {
804 ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); 828 ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
805 return nullptr; 829 return nullptr;
806 } 830 }
807 NextNChars(kNullLen - 1); 831 NextNChars(kNullLen - 1);
808 return MakeUnique<Value>(); 832 return MakeUnique<Value>();
809 } 833 }
810 default: 834 default:
811 ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); 835 ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
812 return nullptr; 836 return nullptr;
(...skipping 17 matching lines...) Expand all
830 const std::string& description) { 854 const std::string& description) {
831 if (line || column) { 855 if (line || column) {
832 return StringPrintf("Line: %i, column: %i, %s", 856 return StringPrintf("Line: %i, column: %i, %s",
833 line, column, description.c_str()); 857 line, column, description.c_str());
834 } 858 }
835 return description; 859 return description;
836 } 860 }
837 861
838 } // namespace internal 862 } // namespace internal
839 } // namespace base 863 } // namespace base
OLDNEW
« no previous file with comments | « no previous file | base/json/json_parser_unittest.cc » ('j') | base/json/json_parser_unittest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698