FSTFuzzTestsPrincipal.mm 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*
  2. * Copyright 2018 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <Foundation/Foundation.h>
  17. #include <cstdlib>
  18. #include <string>
  19. #include <unordered_map>
  20. #include <vector>
  21. #include "Firestore/Example/FuzzTests/FuzzingTargets/FSTFuzzTestFieldPath.h"
  22. #include "Firestore/Example/FuzzTests/FuzzingTargets/FSTFuzzTestSerializer.h"
  23. #include "Firestore/core/src/util/log.h"
  24. #include "Firestore/core/src/util/string_apple.h"
  25. #include "LibFuzzer/FuzzerDefs.h"
  26. #include "absl/strings/str_join.h"
  27. namespace {
  28. using firebase::firestore::util::MakeString;
  29. namespace fuzzing = firebase::firestore::fuzzing;
  30. // A list of targets to fuzz test. Should be kept in sync with
  31. // GetFuzzingTarget() fuzzing_target_names map object.
  32. enum class FuzzingTarget { kNone, kSerializer, kFieldPath };
  33. // Directory to which crashing inputs are written. Must include the '/' at the
  34. // end because libFuzzer prepends this path to the crashing input file name.
  35. // We write crashes to the temporary directory that is available to the iOS app.
  36. NSString *kCrashingInputsDirectory = NSTemporaryDirectory();
  37. // Retrieves the fuzzing target from the FUZZING_TARGET environment variable.
  38. // Default target is kNone if the environment variable is empty, not set, or
  39. // could not be interpreted. Should be kept in sync with FuzzingTarget.
  40. FuzzingTarget GetFuzzingTarget() {
  41. std::unordered_map<std::string, FuzzingTarget> fuzzing_target_names;
  42. fuzzing_target_names["NONE"] = FuzzingTarget::kNone;
  43. fuzzing_target_names["SERIALIZER"] = FuzzingTarget::kSerializer;
  44. fuzzing_target_names["FIELDPATH"] = FuzzingTarget::kFieldPath;
  45. const char *fuzzing_target_env = std::getenv("FUZZING_TARGET");
  46. if (!fuzzing_target_env) {
  47. LOG_WARN("No value provided for FUZZING_TARGET environment variable.");
  48. return FuzzingTarget::kNone;
  49. }
  50. // Convert fuzzing_target_env to std::string after verifying it is not null.
  51. std::string fuzzing_target{fuzzing_target_env};
  52. if (fuzzing_target.empty()) {
  53. LOG_WARN("No value provided for FUZZING_TARGET environment variable.");
  54. return FuzzingTarget::kNone;
  55. }
  56. // Return the value of the fuzzing_target key if it exists in the
  57. // fuzzing_target_names map.
  58. if (fuzzing_target_names.find(fuzzing_target) != fuzzing_target_names.end()) {
  59. return fuzzing_target_names[fuzzing_target];
  60. }
  61. // If the target is not found, print an error message with all available targets.
  62. // The targets must be enclosed in curly brackets and separated by spaces. This format
  63. // is needed by the script /firebase-ios-sdk/script/fuzzing_travis.sh, which parses
  64. // this message to retrieve a list of the available targets.
  65. std::vector<std::string> all_keys;
  66. for (const auto &kv : fuzzing_target_names) {
  67. all_keys.push_back(kv.first);
  68. }
  69. const std::string all_keys_str = absl::StrJoin(all_keys, " ");
  70. LOG_WARN("Invalid fuzzing target: %s. Available targets: { %s }.", fuzzing_target, all_keys_str);
  71. return FuzzingTarget::kNone;
  72. }
  73. // Retrieves fuzzing duration from the FUZZING_DURATION environment variable.
  74. // Defaults to "0" if the environment variable is empty, which corresponds to
  75. // running indefinitely.
  76. std::string GetFuzzingDuration() {
  77. const char *fuzzing_duration_env = std::getenv("FUZZING_DURATION");
  78. if (!fuzzing_duration_env) {
  79. return "0";
  80. }
  81. return std::string{fuzzing_duration_env};
  82. }
  83. // Simulates calling the main() function of libFuzzer (FuzzerMain.cpp).
  84. // Uses GetFuzzingTarget() to get the fuzzing target and sets libFuzzer's args
  85. // accordingly. It also calls an appropriate LLVMFuzzerTestOneInput-like method
  86. // for the defined target.
  87. int RunFuzzTestingMain() {
  88. // Get the fuzzing target.
  89. FuzzingTarget fuzzing_target = GetFuzzingTarget();
  90. // All fuzzing resources.
  91. std::string resources_location = "Firestore_FuzzTests_iOS.xctest/FuzzingResources";
  92. // The dictionary location for the fuzzing target.
  93. std::string dict_location;
  94. // The corpus location for the fuzzing target.
  95. std::string corpus_location;
  96. // Fuzzing target method, equivalent to LLVMFuzzerTestOneInput. Holds a pointer
  97. // to the fuzzing method that is called repeatedly by the fuzzing driver with
  98. // different inputs. Any method assigned to this variable must have the same
  99. // signature as LLVMFuzzerTestOneInput: int(const uint8_t*, size_t).
  100. fuzzer::UserCallback fuzzer_function;
  101. // Set the dictionary and corpus locations according to the fuzzing target.
  102. switch (fuzzing_target) {
  103. case FuzzingTarget::kSerializer:
  104. dict_location = fuzzing::GetSerializerDictionaryLocation(resources_location);
  105. corpus_location = fuzzing::GetSerializerCorpusLocation();
  106. fuzzer_function = fuzzing::FuzzTestDeserialization;
  107. break;
  108. case FuzzingTarget::kFieldPath:
  109. dict_location = fuzzing::GetFieldPathDictionaryLocation(resources_location);
  110. corpus_location = fuzzing::GetFieldPathCorpusLocation(resources_location);
  111. fuzzer_function = fuzzing::FuzzTestFieldPath;
  112. break;
  113. case FuzzingTarget::kNone:
  114. default:
  115. LOG_WARN("Not going to run fuzzing, exiting!");
  116. return 0;
  117. }
  118. // Get dictionary and corpus paths from resources and convert to program arguments.
  119. NSString *plugins_path = [[NSBundle mainBundle] builtInPlugInsPath];
  120. std::string dict_path = MakeString(plugins_path) + "/" + dict_location;
  121. std::string dict_arg = std::string("-dict=") + dict_path;
  122. // No argument prefix required for corpus arg.
  123. std::string corpus_arg = MakeString(plugins_path) + "/" + corpus_location;
  124. // The directory in which libFuzzer writes crashing inputs.
  125. std::string prefix_arg = std::string("-artifact_prefix=") + MakeString(kCrashingInputsDirectory);
  126. // Run fuzzing for the defined fuzzing duration.
  127. std::string time_arg = "-max_total_time=" + GetFuzzingDuration();
  128. // Arguments to libFuzzer main() function should be added to this array,
  129. // e.g., dictionaries, corpus, number of runs, jobs, etc. The FuzzerDriver of
  130. // libFuzzer expects the non-const argument 'char ***argv' and it does not
  131. // modify it throughout the method.
  132. char *program_args[] = {
  133. const_cast<char *>("RunFuzzTestingMain"), // First arg is program name.
  134. const_cast<char *>(prefix_arg.c_str()), // Crashing inputs directory.
  135. const_cast<char *>(time_arg.c_str()), // Maximum total time.
  136. const_cast<char *>(dict_arg.c_str()), // Dictionary arg.
  137. const_cast<char *>(corpus_arg.c_str()) // Corpus must be the last arg.
  138. };
  139. char **argv = program_args;
  140. int argc = sizeof(program_args) / sizeof(program_args[0]);
  141. // Start fuzzing using libFuzzer's driver.
  142. return fuzzer::FuzzerDriver(&argc, &argv, fuzzer_function);
  143. }
  144. } // namespace
  145. /**
  146. * This class is registered as the NSPrincipalClass in the
  147. * Firestore_FuzzTests_iOS bundle's Info.plist. XCTest instantiates this class
  148. * to perform one-time setup for the test bundle, as documented here:
  149. *
  150. * https://developer.apple.com/documentation/xctest/xctestobservationcenter
  151. */
  152. @interface FSTFuzzTestsPrincipal : NSObject
  153. @end
  154. @implementation FSTFuzzTestsPrincipal
  155. - (instancetype)init {
  156. self = [super init];
  157. RunFuzzTestingMain();
  158. return self;
  159. }
  160. @end