FSTFuzzTestsPrincipal.mm 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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/NSObject.h>
  17. #include "LibFuzzer/FuzzerDefs.h"
  18. #include "Firestore/Example/FuzzTests/FuzzingTargets/FSTFuzzTestSerializer.h"
  19. #include "Firestore/core/src/firebase/firestore/util/log.h"
  20. #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
  21. namespace {
  22. using firebase::firestore::util::MakeString;
  23. namespace fuzzing = firebase::firestore::fuzzing;
  24. // A list of targets to fuzz test. Should be kept in sync with the method
  25. // GetFuzzingTarget().
  26. enum class FuzzingTarget { kNone, kSerializer };
  27. // Directory to which crashing inputs are written. Must include the '/' at the
  28. // end because libFuzzer prepends this path to the crashing input file name.
  29. // We write crashes to the temporary directory that is available to the iOS app.
  30. NSString *kCrashingInputsDirectory = NSTemporaryDirectory();
  31. // Retrieves the fuzzing target from the FUZZING_TARGET environment variable.
  32. // Default target is kNone if the environment variable is empty, not set, or
  33. // could not be interpreted. Should be kept in sync with FuzzingTarget.
  34. FuzzingTarget GetFuzzingTarget() {
  35. char *fuzzing_target_env = std::getenv("FUZZING_TARGET");
  36. if (fuzzing_target_env == nullptr) {
  37. LOG_WARN("No value provided for FUZZING_TARGET environment variable.");
  38. return FuzzingTarget::kNone;
  39. }
  40. // Convert fuzzing_target_env to std::string after verifying it is not null.
  41. std::string fuzzing_target{fuzzing_target_env};
  42. if (fuzzing_target.empty()) {
  43. LOG_WARN("No value provided for FUZZING_TARGET environment variable.");
  44. return FuzzingTarget::kNone;
  45. }
  46. if (fuzzing_target == "NONE") {
  47. return FuzzingTarget::kNone;
  48. }
  49. if (fuzzing_target == "SERIALIZER") {
  50. return FuzzingTarget::kSerializer;
  51. }
  52. LOG_WARN("Invalid fuzzing target: %s", fuzzing_target);
  53. return FuzzingTarget::kNone;
  54. }
  55. // Simulates calling the main() function of libFuzzer (FuzzerMain.cpp).
  56. // Uses GetFuzzingTarget() to get the fuzzing target and sets libFuzzer's args
  57. // accordingly. It also calls an appropriate LLVMFuzzerTestOneInput-like method
  58. // for the defined target.
  59. int RunFuzzTestingMain() {
  60. // Get the fuzzing target.
  61. FuzzingTarget fuzzing_target = GetFuzzingTarget();
  62. // All fuzzing resources.
  63. NSString *resources_location = @"Firestore_FuzzTests_iOS.xctest/FuzzingResources";
  64. // The dictionary location for the fuzzing target.
  65. NSString *dict_location;
  66. // The corpus location for the fuzzing target.
  67. NSString *corpus_location;
  68. // Fuzzing target method, equivalent to LLVMFuzzerTestOneInput. Holds a pointer
  69. // to the fuzzing method that is called repeatedly by the fuzzing driver with
  70. // different inputs. Any method assigned to this variable must have the same
  71. // signature as LLVMFuzzerTestOneInput: int(const uint8_t*, size_t).
  72. fuzzer::UserCallback fuzzer_function;
  73. // Set the dictionary and corpus locations according to the fuzzing target.
  74. switch (fuzzing_target) {
  75. case FuzzingTarget::kSerializer:
  76. dict_location = fuzzing::GetSerializerDictionaryLocation(resources_location);
  77. corpus_location = fuzzing::GetSerializerCorpusLocation();
  78. fuzzer_function = fuzzing::FuzzTestDeserialization;
  79. break;
  80. case FuzzingTarget::kNone:
  81. default:
  82. LOG_WARN("Not going to run fuzzing, exiting!");
  83. return 0;
  84. }
  85. // Get dictionary and corpus paths from resources and convert to program arguments.
  86. NSString *plugins_path = [[NSBundle mainBundle] builtInPlugInsPath];
  87. NSString *dict_path = [plugins_path stringByAppendingPathComponent:dict_location];
  88. std::string dict_arg = std::string("-dict=") + MakeString(dict_path);
  89. NSString *corpus_path = [plugins_path stringByAppendingPathComponent:corpus_location];
  90. std::string corpus_arg = MakeString(corpus_path);
  91. // The directory in which libFuzzer writes crashing inputs.
  92. std::string prefix_arg = std::string("-artifact_prefix=") + MakeString(kCrashingInputsDirectory);
  93. // Arguments to libFuzzer main() function should be added to this array,
  94. // e.g., dictionaries, corpus, number of runs, jobs, etc. The FuzzerDriver of
  95. // libFuzzer expects the non-const argument 'char ***argv' and it does not
  96. // modify it throughout the method.
  97. char *program_args[] = {
  98. const_cast<char *>("RunFuzzTestingMain"), // First arg is program name.
  99. const_cast<char *>(prefix_arg.c_str()), // Crashing inputs directory.
  100. const_cast<char *>(dict_arg.c_str()), // Dictionary arg.
  101. const_cast<char *>(corpus_arg.c_str()) // Corpus must be the last arg.
  102. };
  103. char **argv = program_args;
  104. int argc = sizeof(program_args) / sizeof(program_args[0]);
  105. // Start fuzzing using libFuzzer's driver.
  106. return fuzzer::FuzzerDriver(&argc, &argv, fuzzer_function);
  107. }
  108. } // namespace
  109. /**
  110. * This class is registered as the NSPrincipalClass in the
  111. * Firestore_FuzzTests_iOS bundle's Info.plist. XCTest instantiates this class
  112. * to perform one-time setup for the test bundle, as documented here:
  113. *
  114. * https://developer.apple.com/documentation/xctest/xctestobservationcenter
  115. */
  116. @interface FSTFuzzTestsPrincipal : NSObject
  117. @end
  118. @implementation FSTFuzzTestsPrincipal
  119. - (instancetype)init {
  120. self = [super init];
  121. RunFuzzTestingMain();
  122. return self;
  123. }
  124. @end