| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <assert.h> |
| #include <stdlib.h> |
| |
| #include <iostream> |
| |
| // Includes copied from url_parse_fuzzer.cc |
| #include "base/at_exit.h" |
| #include "base/i18n/icu_util.h" |
| #include "url/gurl.h" |
| |
| // Includes *not* copied from url_parse_fuzzer.cc |
| // Contains DEFINE_BINARY_PROTO_FUZZER, a macro we use to define our target |
| // function. |
| #include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h" |
| // Header information about the Protocol Buffer Url class. |
| #include "testing/libfuzzer/fuzzers/url.pb.h" |
| |
| // The code using TestCase is copied from url_parse_fuzzer.cc |
| struct TestCase { |
| TestCase() { |
| CHECK(base::i18n::InitializeICU()); |
| } |
| // used by ICU integration. |
| base::AtExitManager at_exit_manager; |
| }; |
| |
| TestCase* test_case = new TestCase(); |
| |
| // Silence logging from the protobuf library. |
| protobuf_mutator::protobuf::LogSilencer log_silencer; |
| |
| using namespace url_parse_proto_fuzzer; |
| |
| std::string Slash_to_string(int slash) { |
| if (slash == Url::NONE) |
| return ""; |
| if (slash == Url::FORWARD) |
| return "/"; |
| if (slash == Url::BACKWARD) { |
| return "\\"; |
| } |
| assert(false && "Received unexpected value for slash"); |
| // Silence compiler warning about not returning in non-void function. |
| return ""; |
| } |
| |
| // Converts a URL in Protocol Buffer format to a url in string format. |
| // Since protobuf is a relatively simple format, fuzzing targets that do not |
| // accept protobufs (such as this one) will require code to convert from |
| // protobuf to the accepted format (string in this case). |
| std::string protobuf_to_string(const Url& url) { |
| // Build url_string piece by piece from url and then return it. |
| std::string url_string = std::string(""); |
| |
| if (url.has_scheme()) { // Get the scheme if Url has it. |
| // Append the scheme to the url. This may be empty. Then append a colon |
| // which is mandatory if there is a scheme. |
| url_string += url.scheme() + ":"; |
| } |
| |
| // Just append the slashes without doing validation, since it would be too |
| // complex. libFuzzer will hopefully figure out good values. |
| for (const int slash : url.slashes()) |
| url_string += Slash_to_string(slash); |
| |
| // Get host. This is simple since hosts are simply strings according to our |
| // definition. |
| if (url.has_host()) { |
| // Get userinfo if libFuzzer set it. Ensure that user is seperated |
| // from the password by ":" (if a password is included) and that userinfo is |
| // separated from the host by "@". |
| if (url.has_userinfo()) { |
| url_string += url.userinfo().user(); |
| if (url.userinfo().has_password()) { |
| url_string += ":"; |
| url_string += url.userinfo().password(); |
| } |
| url_string += "@"; |
| } |
| url_string += url.host(); |
| |
| // As explained in url.proto, if libFuzzer included a port in url ensure |
| // that it is preceded by the host and then ":". |
| if (url.has_port()) |
| // Convert url.port() from an unsigned 32 bit int before appending it. |
| url_string += ":" + std::to_string(url.port()); |
| } |
| |
| // Append the path segments to the url, with each segment separated by |
| // the path_separator. |
| bool first_segment = true; |
| std::string path_separator = Slash_to_string(url.path_separator()); |
| for (const std::string& path_segment : url.path()) { |
| // There does not need to be a path, but if there is a path and a host, |
| // ensure the path begins with "/". |
| if (url.has_host() && first_segment) { |
| url_string += "/" + path_segment; |
| first_segment = false; |
| } else |
| url_string += path_separator + path_segment; |
| } |
| |
| // Queries must be started by "?". If libFuzzer included a query in url, |
| // ensure that it is preceded by "?". Also Seperate query components with |
| // ampersands as is the convention. |
| bool first_component = true; |
| for (const std::string& query_component : url.query()) { |
| if (first_component) { |
| url_string += "?" + query_component; |
| first_component = false; |
| } else |
| url_string += "&" + query_component; |
| } |
| |
| // Fragments must be started by "#". If libFuzzer included a fragment |
| // in url, ensure that it is preceded by "#". |
| if (url.has_fragment()) |
| url_string += "#" + url.fragment(); |
| |
| return url_string; |
| } |
| |
| // The target function. This is the equivalent of LLVMFuzzerTestOneInput in |
| // typical libFuzzer based fuzzers. It is passed our Url protobuf object that |
| // was mutated by libFuzzer, converts it to a string and then feeds it to url() |
| // for fuzzing. |
| DEFINE_BINARY_PROTO_FUZZER(const Url& url_protobuf) { |
| std::string url_string = protobuf_to_string(url_protobuf); |
| |
| // Allow native input to be retrieved easily. |
| // Note that there will be a trailing newline that is not part of url_string. |
| if (getenv("LPM_DUMP_NATIVE_INPUT")) |
| std::cout << url_string << std::endl; |
| |
| GURL url(url_string); |
| } |