1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
// Copyright 2017 The Chromium Authors
// 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"
// clang-format off
#include "base/strings/string_number_conversions.h"
// clang-format on
// 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/proto/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();
std::string Slash_to_string(int slash) {
if (slash == url_proto::Url::NONE)
return "";
if (slash == url_proto::Url::FORWARD)
return "/";
if (slash == url_proto::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_proto::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 += ":" + base::NumberToString(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_proto::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);
}
|