• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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