url_parse_proto_fuzzer.cc 4.8 KB

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