From 48323d8ea6239b59b7d47de5999cf1114f1fc97b Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Mon, 15 Nov 2021 11:52:35 -0800 Subject: [PATCH] Ensure JSON parser can consume dumped JSON (#27994) * Ensure JSON parser can consume dumped JSON * fixes * fixes * Update fuzzer.cc --- src/core/lib/json/json_reader.cc | 108 +++++++++++++++++++++---------- test/core/json/fuzzer.cc | 8 ++- test/core/json/json_test.cc | 4 +- 3 files changed, 82 insertions(+), 38 deletions(-) diff --git a/src/core/lib/json/json_reader.cc b/src/core/lib/json/json_reader.cc index b8425f284d2..a82577da064 100644 --- a/src/core/lib/json/json_reader.cc +++ b/src/core/lib/json/json_reader.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 errors_; bool truncated_errors_ = false; + uint8_t utf8_bytes_remaining_ = 0; Json root_value_; std::vector 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(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( (unicode_high_surrogate_ - 0xd800) * 0x400); utf32 += static_cast(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': diff --git a/test/core/json/fuzzer.cc b/test/core/json/fuzzer.cc index 2925611c330..bb9df5d5f18 100644 --- a/test/core/json/fuzzer.cc +++ b/test/core/json/fuzzer.cc @@ -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(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; } diff --git a/test/core/json/json_test.cc b/test/core/json/json_test.cc index 6821648c52c..34e2ac10a92 100644 --- a/test/core/json/json_test.cc +++ b/test/core/json/json_test.cc @@ -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) {