Repository navigation
OSS-Fuzz: Move fuzzer to upstream repository #1724
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
a7b66ab
a7626c7
ba86568
b22d10e
2247d19
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| add_subdirectory(lib_json) | ||
| if(JSONCPP_WITH_TESTS) | ||
| add_subdirectory(jsontestrunner) | ||
| endif() | ||
| if(JSONCPP_WITH_TESTS OR JSONCPP_WITH_FUZZING) | ||
| add_subdirectory(test_lib_json) | ||
| endif() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| # vim: et ts=4 sts=4 sw=4 tw=0 | ||
|
|
||
| if(JSONCPP_WITH_TESTS) | ||
|
|
||
| add_executable(jsoncpp_test | ||
| jsontest.cpp | ||
| jsontest.h | ||
|
|
@@ -37,3 +39,44 @@ if(JSONCPP_WITH_POST_BUILD_UNITTEST) | |
| COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $<TARGET_FILE:jsoncpp_test> | ||
| ) | ||
| endif() | ||
|
|
||
| endif() # JSONCPP_WITH_TESTS | ||
|
|
||
| # Standalone fuzz target (used by OSS-Fuzz). The JSONCPP_WITH_FUZZING option | ||
| # is declared in the top-level CMakeLists.txt, so the fuzz target can also be | ||
| # built with -DJSONCPP_WITH_TESTS=OFF. | ||
| # | ||
| # Requires a fuzzing engine: compile everything with the engine's coverage | ||
| # instrumentation only (e.g. -DCMAKE_CXX_FLAGS="-fsanitize=fuzzer-no-link") | ||
| # and link the engine itself into the fuzz target | ||
| # (e.g. -DJSONCPP_FUZZ_ENGINE="-fsanitize=fuzzer"), so that the engine's | ||
| # main() is not pulled into compiler checks and other executables. | ||
| # | ||
| # The libprotobuf-mutator based fuzzer (jsoncpp_fuzz_proto.cc, json.proto, | ||
| # json_proto_converter.*) is built by OSS-Fuzz separately with LPM; the | ||
| # sources are kept here so they live with the other jsoncpp fuzzers. | ||
| set(JSONCPP_FUZZ_ENGINE "" CACHE STRING | ||
| "Fuzzing engine link flags for jsoncpp_fuzzer, e.g. -fsanitize=fuzzer") | ||
| if(JSONCPP_WITH_FUZZING) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nothing builds this target yet, since the OSS-Fuzz build.sh still compiles |
||
| add_executable(jsoncpp_fuzzer | ||
| fuzz.cpp | ||
| fuzz.h | ||
| ) | ||
| if(BUILD_SHARED_LIBS) | ||
| target_link_libraries(jsoncpp_fuzzer jsoncpp_lib) | ||
| else() | ||
| target_link_libraries(jsoncpp_fuzzer jsoncpp_static) | ||
|
Comment on lines
+65
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With Knowledge Base Used: CMake, Meson, and Bazel integration |
||
| endif() | ||
| if(JSONCPP_FUZZ_ENGINE) | ||
| set_target_properties(jsoncpp_fuzzer PROPERTIES | ||
| LINK_FLAGS "${JSONCPP_FUZZ_ENGINE}" | ||
| ) | ||
| endif() | ||
| # Ship the dictionary next to the fuzzer binary. | ||
| add_custom_command(TARGET jsoncpp_fuzzer | ||
| POST_BUILD | ||
| COMMAND ${CMAKE_COMMAND} -E copy_if_different | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/fuzz.dict | ||
| $<TARGET_FILE_DIR:jsoncpp_fuzzer>/jsoncpp_fuzzer.dict | ||
| ) | ||
| endif() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| // Copyright 2020 Google Inc. | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| // | ||
| //////////////////////////////////////////////////////////////////////////////// | ||
|
|
||
| // The proto definition for JSON format has been written based on | ||
| // http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf | ||
|
|
||
| syntax = "proto2"; | ||
|
|
||
| package json_proto; | ||
|
|
||
| message JsonParseAPI { | ||
| required int32 settings = 1; | ||
| required JsonObject object_value = 2; | ||
| } | ||
|
|
||
| message JsonObject { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since |
||
| required int32 settings = 1; | ||
| required string name = 2; | ||
| required JsonValue value = 3; | ||
| } | ||
|
|
||
| message JsonValue { | ||
| oneof value { | ||
| // Json value types: | ||
|
|
||
| // null: null, will be used when 'oneof' contains nothing | ||
|
|
||
| // object: another json object of any type | ||
| JsonObject object_value = 1; | ||
|
|
||
| // array: an array of values | ||
| ArrayValue array_value = 2; | ||
|
|
||
| // number: can be an integer, a float, an exponent | ||
| NumberValue number_value = 3; | ||
|
|
||
| // string: unicode string | ||
| StringValue string_value = 4; | ||
|
|
||
| // boolean: true or talse | ||
| BooleanValue boolean_value = 5; | ||
| } | ||
| } | ||
|
|
||
| message ArrayValue { | ||
| repeated JsonValue value = 1; | ||
| } | ||
|
|
||
| message NumberInteger { | ||
| required int64 value = 1; | ||
| } | ||
|
|
||
| message NumberFloat { | ||
| required double value = 1; | ||
| } | ||
|
|
||
| message NumberExponent { | ||
| required int32 base = 1; | ||
| required int32 exponent = 2; | ||
| required bool use_uppercase = 3; | ||
| } | ||
|
|
||
| message NumberExponentFrac { | ||
| required float base = 1; | ||
| required int32 exponent = 2; | ||
| required bool use_uppercase = 3; | ||
| } | ||
|
|
||
| message NumberValue { | ||
| required NumberInteger integer_value = 1; | ||
|
|
||
| // integer_value is used when oneof field below has nothing. | ||
| oneof value { | ||
| NumberFloat float_value = 2; | ||
| NumberExponent exponent_value = 3; | ||
| NumberExponentFrac exponent_frac_value = 4; | ||
| } | ||
| } | ||
|
|
||
| message StringValue { | ||
| required string value = 1; | ||
| } | ||
|
|
||
| message BooleanValue { | ||
| required bool value = 1; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| // Copyright 2020 Google Inc. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not morally opposed to landing this PR, however introducing Apache licensed code has some licensing headaches that I would prefer to avoid. Can you relicense this as a new file matching JsonCpp's existing licensing? it is allowed under Google policy AFAIK. |
||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| // | ||
| //////////////////////////////////////////////////////////////////////////////// | ||
|
|
||
| #include "json_proto_converter.h" | ||
|
|
||
| #include <charconv> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Floating-point |
||
| #include <cmath> | ||
|
|
||
| namespace json_proto { | ||
|
|
||
| // Writes str as a quoted, JSON-escaped string, so that generated quotes, | ||
| // backslashes and control characters cannot break the surrounding structure. | ||
| void JsonProtoConverter::AppendEscapedString(const std::string& str) { | ||
| static const char kHexDigits[] = "0123456789abcdef"; | ||
| data_ << '"'; | ||
| for (char c : str) { | ||
| switch (c) { | ||
| case '"': | ||
| data_ << "\\\""; | ||
| break; | ||
| case '\\': | ||
| data_ << "\\\\"; | ||
| break; | ||
| case '\b': | ||
| data_ << "\\b"; | ||
| break; | ||
| case '\f': | ||
| data_ << "\\f"; | ||
| break; | ||
| case '\n': | ||
| data_ << "\\n"; | ||
| break; | ||
| case '\r': | ||
| data_ << "\\r"; | ||
| break; | ||
| case '\t': | ||
| data_ << "\\t"; | ||
| break; | ||
| default: | ||
| if (static_cast<unsigned char>(c) < 0x20) { | ||
| data_ << "\\u00" << kHexDigits[(c >> 4) & 0xF] | ||
| << kHexDigits[c & 0xF]; | ||
| } else { | ||
| data_ << c; | ||
| } | ||
| break; | ||
| } | ||
| } | ||
| data_ << '"'; | ||
| } | ||
|
|
||
| void JsonProtoConverter::AppendArray(const ArrayValue& array_value) { | ||
| data_ << '['; | ||
| bool need_comma = false; | ||
| for (const auto& value : array_value.value()) { | ||
| // Trailing comma inside of an array makes JSON invalid, avoid adding that. | ||
| if (need_comma) | ||
| data_ << ','; | ||
| else | ||
| need_comma = true; | ||
|
|
||
| AppendValue(value); | ||
| } | ||
| data_ << ']'; | ||
| } | ||
|
|
||
| // Writes value as a decimal number without an exponent: scientific notation | ||
| // would add a stray exponent to the generated number (e.g. "1e+20e3"), while | ||
| // std::fixed with the stream's default precision would truncate small values | ||
| // ("0.0001004" -> "0.000100"), collapsing distinct mutations. to_chars with | ||
| // chars_format::fixed writes the shortest fixed-point string that round-trips | ||
| // the value, avoiding both. Non-finite values have no decimal form; write 0 | ||
| // instead ("inf"/"nan" text would not be a valid JSON token) so the rest of | ||
| // the generated structure remains parseable. | ||
| void JsonProtoConverter::AppendPlainFloat(float value) { | ||
| if (!std::isfinite(value)) { | ||
| data_ << '0'; | ||
| return; | ||
| } | ||
| char buf[96]; // shortest round-trip fixed form of a float fits comfortably | ||
| const auto result = | ||
| std::to_chars(buf, buf + sizeof(buf), value, std::chars_format::fixed); | ||
| if (result.ec == std::errc()) { | ||
| data_.write(buf, result.ptr - buf); | ||
| } else { | ||
| data_ << '0'; | ||
| } | ||
| } | ||
|
|
||
| void JsonProtoConverter::AppendNumber(const NumberValue& number_value) { | ||
| if (number_value.has_float_value()) { | ||
| const double value = number_value.float_value().value(); | ||
| // Keep "inf"/"nan" out of the output; they are not valid JSON numbers. | ||
| data_ << (std::isfinite(value) ? value : 0.0); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This still writes doubles at the stream's default precision of 6 significant digits, which is the same digit loss that was fixed for |
||
| } else if (number_value.has_exponent_value()) { | ||
| auto value = number_value.exponent_value(); | ||
| data_ << value.base(); | ||
| data_ << (value.use_uppercase() ? 'E' : 'e'); | ||
| data_ << value.exponent(); | ||
| } else if (number_value.has_exponent_frac_value()) { | ||
| auto value = number_value.exponent_frac_value(); | ||
|
arthurscchan marked this conversation as resolved.
|
||
| AppendPlainFloat(value.base()); | ||
| data_ << (value.use_uppercase() ? 'E' : 'e'); | ||
| data_ << value.exponent(); | ||
| } else { | ||
| data_ << number_value.integer_value().value(); | ||
| } | ||
| } | ||
|
|
||
| void JsonProtoConverter::AppendObject(const JsonObject& json_object) { | ||
| data_ << '{'; | ||
| AppendEscapedString(json_object.name()); | ||
| data_ << ':'; | ||
| AppendValue(json_object.value()); | ||
| data_ << '}'; | ||
| } | ||
|
|
||
| void JsonProtoConverter::AppendValue(const JsonValue& json_value) { | ||
| if (json_value.has_object_value()) { | ||
| AppendObject(json_value.object_value()); | ||
| } else if (json_value.has_array_value()) { | ||
| AppendArray(json_value.array_value()); | ||
| } else if (json_value.has_number_value()) { | ||
| AppendNumber(json_value.number_value()); | ||
| } else if (json_value.has_string_value()) { | ||
| AppendEscapedString(json_value.string_value().value()); | ||
| } else if (json_value.has_boolean_value()) { | ||
| data_ << (json_value.boolean_value().value() ? "true" : "false"); | ||
| } else { | ||
| data_ << "null"; | ||
| } | ||
| } | ||
|
|
||
| std::string JsonProtoConverter::Convert(const JsonObject& json_object) { | ||
| AppendObject(json_object); | ||
| return data_.str(); | ||
| } | ||
|
|
||
| std::string JsonProtoConverter::Convert( | ||
| const json_proto::ArrayValue& json_array) { | ||
| AppendArray(json_array); | ||
| return data_.str(); | ||
| } | ||
|
|
||
| } // namespace json_proto | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| // Copyright 2020 Google Inc. | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| // | ||
| //////////////////////////////////////////////////////////////////////////////// | ||
|
|
||
| #ifndef JSON_PROTO_CONVERTER_H_ | ||
| #define JSON_PROTO_CONVERTER_H_ | ||
|
|
||
| #include <sstream> | ||
| #include <string> | ||
|
|
||
| #include "json.pb.h" | ||
|
|
||
| namespace json_proto { | ||
|
|
||
| class JsonProtoConverter { | ||
| public: | ||
| std::string Convert(const json_proto::JsonObject&); | ||
| std::string Convert(const json_proto::ArrayValue&); | ||
|
|
||
| private: | ||
| std::stringstream data_; | ||
|
|
||
| void AppendArray(const json_proto::ArrayValue&); | ||
| void AppendEscapedString(const std::string& str); | ||
| void AppendNumber(const json_proto::NumberValue&); | ||
| void AppendPlainFloat(float value); | ||
| void AppendObject(const json_proto::JsonObject&); | ||
| void AppendValue(const json_proto::JsonValue&); | ||
| }; | ||
|
|
||
| } // namespace json_proto | ||
|
|
||
| #endif // TESTING_LIBFUZZER_PROTO_JSON_PROTO_CONVERTER_H_ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: the comment doesn't match the guard name ( |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the sources live here, I'd want CI to build them; otherwise they'll only break in OSS-Fuzz. A CIFuzz workflow would cover both fuzzers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can probably handle this as a follow up if needed.