Ensure JSON parser can consume dumped JSON (#27994)

* Ensure JSON parser can consume dumped JSON

* fixes

* fixes

* Update fuzzer.cc
reviewable/pr27851/r7^2
Craig Tiller 3 years ago committed by GitHub
parent b7311aad77
commit 48323d8ea6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 108
      src/core/lib/json/json_reader.cc
  2. 8
      test/core/json/fuzzer.cc
  3. 4
      test/core/json/json_test.cc

@ -95,8 +95,8 @@ class JsonReader {
size_t CurrentIndex() const { return input_ - original_input_ - 1; }
void StringAddChar(uint32_t c);
void StringAddUtf32(uint32_t c);
GRPC_MUST_USE_RESULT bool StringAddChar(uint32_t c);
GRPC_MUST_USE_RESULT bool StringAddUtf32(uint32_t c);
Json* CreateAndLinkValue();
bool StartContainer(Json::Type type);
@ -119,6 +119,7 @@ class JsonReader {
uint16_t unicode_high_surrogate_ = 0;
std::vector<grpc_error_handle> errors_;
bool truncated_errors_ = false;
uint8_t utf8_bytes_remaining_ = 0;
Json root_value_;
std::vector<Json*> stack_;
@ -127,34 +128,55 @@ class JsonReader {
std::string string_;
};
void JsonReader::StringAddChar(uint32_t c) {
bool JsonReader::StringAddChar(uint32_t c) {
switch (utf8_bytes_remaining_) {
case 0:
if ((c & 0x80) == 0) {
utf8_bytes_remaining_ = 0;
} else if ((c & 0xe0) == 0xc0) {
utf8_bytes_remaining_ = 1;
} else if ((c & 0xf0) == 0xe0) {
utf8_bytes_remaining_ = 2;
} else if ((c & 0xf8) == 0xf0) {
utf8_bytes_remaining_ = 3;
} else {
return false;
}
break;
case 1:
case 2:
case 3:
if ((c & 0xc0) != 0x80) return false;
--utf8_bytes_remaining_;
break;
default:
abort();
}
string_.push_back(static_cast<uint8_t>(c));
return true;
}
void JsonReader::StringAddUtf32(uint32_t c) {
bool JsonReader::StringAddUtf32(uint32_t c) {
if (c <= 0x7f) {
StringAddChar(c);
return StringAddChar(c);
} else if (c <= 0x7ff) {
uint32_t b1 = 0xc0 | ((c >> 6) & 0x1f);
uint32_t b2 = 0x80 | (c & 0x3f);
StringAddChar(b1);
StringAddChar(b2);
return StringAddChar(b1) && StringAddChar(b2);
} else if (c <= 0xffff) {
uint32_t b1 = 0xe0 | ((c >> 12) & 0x0f);
uint32_t b2 = 0x80 | ((c >> 6) & 0x3f);
uint32_t b3 = 0x80 | (c & 0x3f);
StringAddChar(b1);
StringAddChar(b2);
StringAddChar(b3);
return StringAddChar(b1) && StringAddChar(b2) && StringAddChar(b3);
} else if (c <= 0x1fffff) {
uint32_t b1 = 0xf0 | ((c >> 18) & 0x07);
uint32_t b2 = 0x80 | ((c >> 12) & 0x3f);
uint32_t b3 = 0x80 | ((c >> 6) & 0x3f);
uint32_t b4 = 0x80 | (c & 0x3f);
StringAddChar(b1);
StringAddChar(b2);
StringAddChar(b3);
StringAddChar(b4);
return StringAddChar(b1) && StringAddChar(b2) && StringAddChar(b3) &&
StringAddChar(b4);
} else {
return false;
}
}
@ -275,6 +297,18 @@ JsonReader::Status JsonReader::Run() {
switch (c) {
/* Let's process the error case first. */
case GRPC_JSON_READ_CHAR_EOF:
switch (state_) {
case State::GRPC_JSON_STATE_VALUE_NUMBER:
case State::GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
case State::GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
case State::GRPC_JSON_STATE_VALUE_NUMBER_EPM:
if (!SetNumber()) return Status::GRPC_JSON_PARSE_ERROR;
state_ = State::GRPC_JSON_STATE_VALUE_END;
break;
default:
break;
}
if (IsComplete()) {
return Status::GRPC_JSON_DONE;
}
@ -299,7 +333,7 @@ JsonReader::Status JsonReader::Run() {
if (unicode_high_surrogate_ != 0) {
return Status::GRPC_JSON_PARSE_ERROR;
}
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
break;
case State::GRPC_JSON_STATE_VALUE_NUMBER:
@ -325,7 +359,7 @@ JsonReader::Status JsonReader::Run() {
if (unicode_high_surrogate_ != 0) {
return Status::GRPC_JSON_PARSE_ERROR;
}
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
break;
case State::GRPC_JSON_STATE_VALUE_NUMBER:
@ -410,7 +444,7 @@ JsonReader::Status JsonReader::Run() {
if (unicode_high_surrogate_ != 0) {
return Status::GRPC_JSON_PARSE_ERROR;
}
StringAddChar('\\');
if (!StringAddChar('\\')) return Status::GRPC_JSON_PARSE_ERROR;
if (escaped_string_was_key_) {
state_ = State::GRPC_JSON_STATE_OBJECT_KEY_STRING;
} else {
@ -440,7 +474,7 @@ JsonReader::Status JsonReader::Run() {
SetKey();
} else {
if (c < 32) return Status::GRPC_JSON_PARSE_ERROR;
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
}
break;
@ -453,7 +487,7 @@ JsonReader::Status JsonReader::Run() {
SetString();
} else {
if (c < 32) return Status::GRPC_JSON_PARSE_ERROR;
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
}
break;
@ -481,7 +515,7 @@ JsonReader::Status JsonReader::Run() {
break;
case '0':
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
state_ = State::GRPC_JSON_STATE_VALUE_NUMBER_ZERO;
break;
@ -495,7 +529,7 @@ JsonReader::Status JsonReader::Run() {
case '8':
case '9':
case '-':
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
state_ = State::GRPC_JSON_STATE_VALUE_NUMBER;
break;
@ -530,22 +564,22 @@ JsonReader::Status JsonReader::Run() {
switch (c) {
case '"':
case '/':
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
break;
case 'b':
StringAddChar('\b');
if (!StringAddChar('\b')) return Status::GRPC_JSON_PARSE_ERROR;
break;
case 'f':
StringAddChar('\f');
if (!StringAddChar('\f')) return Status::GRPC_JSON_PARSE_ERROR;
break;
case 'n':
StringAddChar('\n');
if (!StringAddChar('\n')) return Status::GRPC_JSON_PARSE_ERROR;
break;
case 'r':
StringAddChar('\r');
if (!StringAddChar('\r')) return Status::GRPC_JSON_PARSE_ERROR;
break;
case 't':
StringAddChar('\t');
if (!StringAddChar('\t')) return Status::GRPC_JSON_PARSE_ERROR;
break;
case 'u':
state_ = State::GRPC_JSON_STATE_STRING_ESCAPE_U1;
@ -602,14 +636,18 @@ JsonReader::Status JsonReader::Run() {
utf32 += static_cast<uint32_t>(
(unicode_high_surrogate_ - 0xd800) * 0x400);
utf32 += static_cast<uint32_t>(unicode_char_ - 0xdc00);
StringAddUtf32(utf32);
if (!StringAddUtf32(utf32)) {
return Status::GRPC_JSON_PARSE_ERROR;
}
unicode_high_surrogate_ = 0;
} else {
/* anything else */
if (unicode_high_surrogate_ != 0) {
return Status::GRPC_JSON_PARSE_ERROR;
}
StringAddUtf32(unicode_char_);
if (!StringAddUtf32(unicode_char_)) {
return Status::GRPC_JSON_PARSE_ERROR;
}
}
if (escaped_string_was_key_) {
state_ = State::GRPC_JSON_STATE_OBJECT_KEY_STRING;
@ -623,7 +661,7 @@ JsonReader::Status JsonReader::Run() {
break;
case State::GRPC_JSON_STATE_VALUE_NUMBER:
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
switch (c) {
case '0':
case '1':
@ -649,7 +687,7 @@ JsonReader::Status JsonReader::Run() {
break;
case State::GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
switch (c) {
case '0':
case '1':
@ -673,12 +711,12 @@ JsonReader::Status JsonReader::Run() {
case State::GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
if (c != '.') return Status::GRPC_JSON_PARSE_ERROR;
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
state_ = State::GRPC_JSON_STATE_VALUE_NUMBER_DOT;
break;
case State::GRPC_JSON_STATE_VALUE_NUMBER_DOT:
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
switch (c) {
case '0':
case '1':
@ -698,7 +736,7 @@ JsonReader::Status JsonReader::Run() {
break;
case State::GRPC_JSON_STATE_VALUE_NUMBER_E:
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
switch (c) {
case '0':
case '1':
@ -720,7 +758,7 @@ JsonReader::Status JsonReader::Run() {
break;
case State::GRPC_JSON_STATE_VALUE_NUMBER_EPM:
StringAddChar(c);
if (!StringAddChar(c)) return Status::GRPC_JSON_PARSE_ERROR;
switch (c) {
case '0':
case '1':

@ -30,8 +30,14 @@ bool leak_check = true;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
grpc_error_handle error = GRPC_ERROR_NONE;
grpc_core::Json::Parse(
auto json = grpc_core::Json::Parse(
absl::string_view(reinterpret_cast<const char*>(data), size), &error);
if (error == GRPC_ERROR_NONE) {
auto text2 = json.Dump();
auto json2 = grpc_core::Json::Parse(text2, &error);
GPR_ASSERT(error == GRPC_ERROR_NONE);
GPR_ASSERT(json == json2);
}
GRPC_ERROR_UNREF(error);
return 0;
}

@ -131,8 +131,8 @@ TEST(Json, EscapesAndControlCharactersInKeyStrings) {
}
TEST(Json, WriterCutsOffInvalidUtf8) {
RunSuccessTest("\"abc\xf0\x9d\x24\"", "abc\xf0\x9d\x24", "\"abc\"");
RunSuccessTest("\"\xff\"", "\xff", "\"\"");
EXPECT_EQ(Json("abc\xf0\x9d\x24").Dump(), "\"abc\"");
EXPECT_EQ(Json("\xff").Dump(), "\"\"");
}
TEST(Json, ValidNumbers) {

Loading…
Cancel
Save