1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <assert.h>
6 #include <stdlib.h>
7
8 #include <iostream>
9
10 // Includes copied from url_parse_fuzzer.cc
11 #include "base/at_exit.h"
12 #include "base/i18n/icu_util.h"
13 #include "url/gurl.h"
14
15 // clang-format off
16 #include "base/strings/string_number_conversions.h"
17 // clang-format on
18
19 // Includes *not* copied from url_parse_fuzzer.cc
20 // Contains DEFINE_BINARY_PROTO_FUZZER, a macro we use to define our target
21 // function.
22 #include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h"
23 // Header information about the Protocol Buffer Url class.
24 #include "testing/libfuzzer/proto/url.pb.h"
25
26 // The code using TestCase is copied from url_parse_fuzzer.cc
27 struct TestCase {
TestCaseTestCase28 TestCase() {
29 CHECK(base::i18n::InitializeICU());
30 }
31 // used by ICU integration.
32 base::AtExitManager at_exit_manager;
33 };
34
35 TestCase* test_case = new TestCase();
36
37 // Silence logging from the protobuf library.
38 protobuf_mutator::protobuf::LogSilencer log_silencer;
39
Slash_to_string(int slash)40 std::string Slash_to_string(int slash) {
41 if (slash == url_proto::Url::NONE)
42 return "";
43 if (slash == url_proto::Url::FORWARD)
44 return "/";
45 if (slash == url_proto::Url::BACKWARD) {
46 return "\\";
47 }
48 assert(false && "Received unexpected value for slash");
49 // Silence compiler warning about not returning in non-void function.
50 return "";
51 }
52
53 // Converts a URL in Protocol Buffer format to a url in string format.
54 // Since protobuf is a relatively simple format, fuzzing targets that do not
55 // accept protobufs (such as this one) will require code to convert from
56 // protobuf to the accepted format (string in this case).
protobuf_to_string(const url_proto::Url & url)57 std::string protobuf_to_string(const url_proto::Url& url) {
58 // Build url_string piece by piece from url and then return it.
59 std::string url_string = std::string("");
60
61 if (url.has_scheme()) { // Get the scheme if Url has it.
62 // Append the scheme to the url. This may be empty. Then append a colon
63 // which is mandatory if there is a scheme.
64 url_string += url.scheme() + ":";
65 }
66
67 // Just append the slashes without doing validation, since it would be too
68 // complex. libFuzzer will hopefully figure out good values.
69 for (const int slash : url.slashes())
70 url_string += Slash_to_string(slash);
71
72 // Get host. This is simple since hosts are simply strings according to our
73 // definition.
74 if (url.has_host()) {
75 // Get userinfo if libFuzzer set it. Ensure that user is seperated
76 // from the password by ":" (if a password is included) and that userinfo is
77 // separated from the host by "@".
78 if (url.has_userinfo()) {
79 url_string += url.userinfo().user();
80 if (url.userinfo().has_password()) {
81 url_string += ":";
82 url_string += url.userinfo().password();
83 }
84 url_string += "@";
85 }
86 url_string += url.host();
87
88 // As explained in url.proto, if libFuzzer included a port in url ensure
89 // that it is preceded by the host and then ":".
90 if (url.has_port())
91 // Convert url.port() from an unsigned 32 bit int before appending it.
92 url_string += ":" + base::NumberToString(url.port());
93 }
94
95 // Append the path segments to the url, with each segment separated by
96 // the path_separator.
97 bool first_segment = true;
98 std::string path_separator = Slash_to_string(url.path_separator());
99 for (const std::string& path_segment : url.path()) {
100 // There does not need to be a path, but if there is a path and a host,
101 // ensure the path begins with "/".
102 if (url.has_host() && first_segment) {
103 url_string += "/" + path_segment;
104 first_segment = false;
105 } else
106 url_string += path_separator + path_segment;
107 }
108
109 // Queries must be started by "?". If libFuzzer included a query in url,
110 // ensure that it is preceded by "?". Also Seperate query components with
111 // ampersands as is the convention.
112 bool first_component = true;
113 for (const std::string& query_component : url.query()) {
114 if (first_component) {
115 url_string += "?" + query_component;
116 first_component = false;
117 } else
118 url_string += "&" + query_component;
119 }
120
121 // Fragments must be started by "#". If libFuzzer included a fragment
122 // in url, ensure that it is preceded by "#".
123 if (url.has_fragment())
124 url_string += "#" + url.fragment();
125
126 return url_string;
127 }
128
129 // The target function. This is the equivalent of LLVMFuzzerTestOneInput in
130 // typical libFuzzer based fuzzers. It is passed our Url protobuf object that
131 // was mutated by libFuzzer, converts it to a string and then feeds it to url()
132 // for fuzzing.
DEFINE_BINARY_PROTO_FUZZER(const url_proto::Url & url_protobuf)133 DEFINE_BINARY_PROTO_FUZZER(const url_proto::Url& url_protobuf) {
134 std::string url_string = protobuf_to_string(url_protobuf);
135
136 // Allow native input to be retrieved easily.
137 // Note that there will be a trailing newline that is not part of url_string.
138 if (getenv("LPM_DUMP_NATIVE_INPUT"))
139 std::cout << url_string << std::endl;
140
141 GURL url(url_string);
142 }
143