• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "source/fuzz/fuzzer.h"
16 
17 #include <cassert>
18 #include <memory>
19 #include <sstream>
20 
21 #include "fuzzer_pass_adjust_memory_operands_masks.h"
22 #include "source/fuzz/fact_manager.h"
23 #include "source/fuzz/fuzzer_context.h"
24 #include "source/fuzz/fuzzer_pass_add_access_chains.h"
25 #include "source/fuzz/fuzzer_pass_add_composite_types.h"
26 #include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
27 #include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
28 #include "source/fuzz/fuzzer_pass_add_dead_continues.h"
29 #include "source/fuzz/fuzzer_pass_add_equation_instructions.h"
30 #include "source/fuzz/fuzzer_pass_add_function_calls.h"
31 #include "source/fuzz/fuzzer_pass_add_global_variables.h"
32 #include "source/fuzz/fuzzer_pass_add_loads.h"
33 #include "source/fuzz/fuzzer_pass_add_local_variables.h"
34 #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
35 #include "source/fuzz/fuzzer_pass_add_stores.h"
36 #include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
37 #include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
38 #include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
39 #include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
40 #include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
41 #include "source/fuzz/fuzzer_pass_construct_composites.h"
42 #include "source/fuzz/fuzzer_pass_copy_objects.h"
43 #include "source/fuzz/fuzzer_pass_donate_modules.h"
44 #include "source/fuzz/fuzzer_pass_merge_blocks.h"
45 #include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
46 #include "source/fuzz/fuzzer_pass_outline_functions.h"
47 #include "source/fuzz/fuzzer_pass_permute_blocks.h"
48 #include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
49 #include "source/fuzz/fuzzer_pass_split_blocks.h"
50 #include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
51 #include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
52 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
53 #include "source/fuzz/pseudo_random_generator.h"
54 #include "source/fuzz/transformation_context.h"
55 #include "source/opt/build_module.h"
56 #include "source/spirv_fuzzer_options.h"
57 #include "source/util/make_unique.h"
58 
59 namespace spvtools {
60 namespace fuzz {
61 
62 namespace {
63 const uint32_t kIdBoundGap = 100;
64 
65 const uint32_t kTransformationLimit = 500;
66 
67 const uint32_t kChanceOfApplyingAnotherPass = 85;
68 
69 // A convenience method to add a fuzzer pass to |passes| with probability 0.5.
70 // All fuzzer passes take |ir_context|, |transformation_context|,
71 // |fuzzer_context| and |transformation_sequence_out| as parameters.  Extra
72 // arguments can be provided via |extra_args|.
73 template <typename T, typename... Args>
MaybeAddPass(std::vector<std::unique_ptr<FuzzerPass>> * passes,opt::IRContext * ir_context,TransformationContext * transformation_context,FuzzerContext * fuzzer_context,protobufs::TransformationSequence * transformation_sequence_out,Args &&...extra_args)74 void MaybeAddPass(
75     std::vector<std::unique_ptr<FuzzerPass>>* passes,
76     opt::IRContext* ir_context, TransformationContext* transformation_context,
77     FuzzerContext* fuzzer_context,
78     protobufs::TransformationSequence* transformation_sequence_out,
79     Args&&... extra_args) {
80   if (fuzzer_context->ChooseEven()) {
81     passes->push_back(MakeUnique<T>(ir_context, transformation_context,
82                                     fuzzer_context, transformation_sequence_out,
83                                     std::forward<Args>(extra_args)...));
84   }
85 }
86 
87 }  // namespace
88 
89 struct Fuzzer::Impl {
Implspvtools::fuzz::Fuzzer::Impl90   Impl(spv_target_env env, uint32_t random_seed, bool validate_after_each_pass,
91        spv_validator_options options)
92       : target_env(env),
93         seed(random_seed),
94         validate_after_each_fuzzer_pass(validate_after_each_pass),
95         validator_options(options) {}
96 
97   bool ApplyPassAndCheckValidity(FuzzerPass* pass,
98                                  const opt::IRContext& ir_context,
99                                  const spvtools::SpirvTools& tools) const;
100 
101   const spv_target_env target_env;       // Target environment.
102   MessageConsumer consumer;              // Message consumer.
103   const uint32_t seed;                   // Seed for random number generator.
104   bool validate_after_each_fuzzer_pass;  // Determines whether the validator
105                                          // should be invoked after every fuzzer
106                                          // pass.
107   spv_validator_options validator_options;  // Options to control validation.
108 };
109 
Fuzzer(spv_target_env env,uint32_t seed,bool validate_after_each_fuzzer_pass,spv_validator_options validator_options)110 Fuzzer::Fuzzer(spv_target_env env, uint32_t seed,
111                bool validate_after_each_fuzzer_pass,
112                spv_validator_options validator_options)
113     : impl_(MakeUnique<Impl>(env, seed, validate_after_each_fuzzer_pass,
114                              validator_options)) {}
115 
116 Fuzzer::~Fuzzer() = default;
117 
SetMessageConsumer(MessageConsumer c)118 void Fuzzer::SetMessageConsumer(MessageConsumer c) {
119   impl_->consumer = std::move(c);
120 }
121 
ApplyPassAndCheckValidity(FuzzerPass * pass,const opt::IRContext & ir_context,const spvtools::SpirvTools & tools) const122 bool Fuzzer::Impl::ApplyPassAndCheckValidity(
123     FuzzerPass* pass, const opt::IRContext& ir_context,
124     const spvtools::SpirvTools& tools) const {
125   pass->Apply();
126   if (validate_after_each_fuzzer_pass) {
127     std::vector<uint32_t> binary_to_validate;
128     ir_context.module()->ToBinary(&binary_to_validate, false);
129     if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size(),
130                         validator_options)) {
131       consumer(SPV_MSG_INFO, nullptr, {},
132                "Binary became invalid during fuzzing (set a breakpoint to "
133                "inspect); stopping.");
134       return false;
135     }
136   }
137   return true;
138 }
139 
Run(const std::vector<uint32_t> & binary_in,const protobufs::FactSequence & initial_facts,const std::vector<fuzzerutil::ModuleSupplier> & donor_suppliers,std::vector<uint32_t> * binary_out,protobufs::TransformationSequence * transformation_sequence_out) const140 Fuzzer::FuzzerResultStatus Fuzzer::Run(
141     const std::vector<uint32_t>& binary_in,
142     const protobufs::FactSequence& initial_facts,
143     const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
144     std::vector<uint32_t>* binary_out,
145     protobufs::TransformationSequence* transformation_sequence_out) const {
146   // Check compatibility between the library version being linked with and the
147   // header files being used.
148   GOOGLE_PROTOBUF_VERIFY_VERSION;
149 
150   spvtools::SpirvTools tools(impl_->target_env);
151   tools.SetMessageConsumer(impl_->consumer);
152   if (!tools.IsValid()) {
153     impl_->consumer(SPV_MSG_ERROR, nullptr, {},
154                     "Failed to create SPIRV-Tools interface; stopping.");
155     return Fuzzer::FuzzerResultStatus::kFailedToCreateSpirvToolsInterface;
156   }
157 
158   // Initial binary should be valid.
159   if (!tools.Validate(&binary_in[0], binary_in.size(),
160                       impl_->validator_options)) {
161     impl_->consumer(SPV_MSG_ERROR, nullptr, {},
162                     "Initial binary is invalid; stopping.");
163     return Fuzzer::FuzzerResultStatus::kInitialBinaryInvalid;
164   }
165 
166   // Build the module from the input binary.
167   std::unique_ptr<opt::IRContext> ir_context = BuildModule(
168       impl_->target_env, impl_->consumer, binary_in.data(), binary_in.size());
169   assert(ir_context);
170 
171   // Make a PRNG from the seed passed to the fuzzer on creation.
172   PseudoRandomGenerator random_generator(impl_->seed);
173 
174   // The fuzzer will introduce new ids into the module.  The module's id bound
175   // gives the smallest id that can be used for this purpose.  We add an offset
176   // to this so that there is a sizeable gap between the ids used in the
177   // original module and the ids used for fuzzing, as a readability aid.
178   //
179   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the
180   //  case where the maximum id bound is reached.
181   auto minimum_fresh_id = ir_context->module()->id_bound() + kIdBoundGap;
182   FuzzerContext fuzzer_context(&random_generator, minimum_fresh_id);
183 
184   FactManager fact_manager;
185   fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get());
186   TransformationContext transformation_context(&fact_manager,
187                                                impl_->validator_options);
188 
189   // Add some essential ingredients to the module if they are not already
190   // present, such as boolean constants.
191   FuzzerPassAddUsefulConstructs add_useful_constructs(
192       ir_context.get(), &transformation_context, &fuzzer_context,
193       transformation_sequence_out);
194   if (!impl_->ApplyPassAndCheckValidity(&add_useful_constructs, *ir_context,
195                                         tools)) {
196     return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
197   }
198 
199   // Apply some semantics-preserving passes.
200   std::vector<std::unique_ptr<FuzzerPass>> passes;
201   while (passes.empty()) {
202     MaybeAddPass<FuzzerPassAddAccessChains>(
203         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
204         transformation_sequence_out);
205     MaybeAddPass<FuzzerPassAddCompositeTypes>(
206         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
207         transformation_sequence_out);
208     MaybeAddPass<FuzzerPassAddDeadBlocks>(
209         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
210         transformation_sequence_out);
211     MaybeAddPass<FuzzerPassAddDeadBreaks>(
212         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
213         transformation_sequence_out);
214     MaybeAddPass<FuzzerPassAddDeadContinues>(
215         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
216         transformation_sequence_out);
217     MaybeAddPass<FuzzerPassAddEquationInstructions>(
218         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
219         transformation_sequence_out);
220     MaybeAddPass<FuzzerPassAddFunctionCalls>(
221         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
222         transformation_sequence_out);
223     MaybeAddPass<FuzzerPassAddGlobalVariables>(
224         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
225         transformation_sequence_out);
226     MaybeAddPass<FuzzerPassAddLoads>(&passes, ir_context.get(),
227                                      &transformation_context, &fuzzer_context,
228                                      transformation_sequence_out);
229     MaybeAddPass<FuzzerPassAddLocalVariables>(
230         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
231         transformation_sequence_out);
232     MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
233                                       &transformation_context, &fuzzer_context,
234                                       transformation_sequence_out);
235     MaybeAddPass<FuzzerPassApplyIdSynonyms>(
236         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
237         transformation_sequence_out);
238     MaybeAddPass<FuzzerPassConstructComposites>(
239         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
240         transformation_sequence_out);
241     MaybeAddPass<FuzzerPassCopyObjects>(
242         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
243         transformation_sequence_out);
244     MaybeAddPass<FuzzerPassDonateModules>(
245         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
246         transformation_sequence_out, donor_suppliers);
247     MaybeAddPass<FuzzerPassMergeBlocks>(
248         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
249         transformation_sequence_out);
250     MaybeAddPass<FuzzerPassObfuscateConstants>(
251         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
252         transformation_sequence_out);
253     MaybeAddPass<FuzzerPassOutlineFunctions>(
254         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
255         transformation_sequence_out);
256     MaybeAddPass<FuzzerPassPermuteBlocks>(
257         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
258         transformation_sequence_out);
259     MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
260         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
261         transformation_sequence_out);
262     MaybeAddPass<FuzzerPassSplitBlocks>(
263         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
264         transformation_sequence_out);
265   }
266 
267   bool is_first = true;
268   while (static_cast<uint32_t>(
269              transformation_sequence_out->transformation_size()) <
270              kTransformationLimit &&
271          (is_first ||
272           fuzzer_context.ChoosePercentage(kChanceOfApplyingAnotherPass))) {
273     is_first = false;
274     if (!impl_->ApplyPassAndCheckValidity(
275             passes[fuzzer_context.RandomIndex(passes)].get(), *ir_context,
276             tools)) {
277       return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
278     }
279   }
280 
281   // Now apply some passes that it does not make sense to apply repeatedly,
282   // as they do not unlock other passes.
283   std::vector<std::unique_ptr<FuzzerPass>> final_passes;
284   MaybeAddPass<FuzzerPassAdjustFunctionControls>(
285       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
286       transformation_sequence_out);
287   MaybeAddPass<FuzzerPassAdjustLoopControls>(
288       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
289       transformation_sequence_out);
290   MaybeAddPass<FuzzerPassAdjustMemoryOperandsMasks>(
291       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
292       transformation_sequence_out);
293   MaybeAddPass<FuzzerPassAdjustSelectionControls>(
294       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
295       transformation_sequence_out);
296   MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
297       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
298       transformation_sequence_out);
299   MaybeAddPass<FuzzerPassSwapCommutableOperands>(
300       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
301       transformation_sequence_out);
302   MaybeAddPass<FuzzerPassToggleAccessChainInstruction>(
303       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
304       transformation_sequence_out);
305   for (auto& pass : final_passes) {
306     if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
307       return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
308     }
309   }
310 
311   // Encode the module as a binary.
312   ir_context->module()->ToBinary(binary_out, false);
313 
314   return Fuzzer::FuzzerResultStatus::kComplete;
315 }
316 
317 }  // namespace fuzz
318 }  // namespace spvtools
319