• 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_pass.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19 #include "source/fuzz/transformation_add_constant_boolean.h"
20 #include "source/fuzz/transformation_add_constant_composite.h"
21 #include "source/fuzz/transformation_add_constant_scalar.h"
22 #include "source/fuzz/transformation_add_global_undef.h"
23 #include "source/fuzz/transformation_add_type_boolean.h"
24 #include "source/fuzz/transformation_add_type_float.h"
25 #include "source/fuzz/transformation_add_type_int.h"
26 #include "source/fuzz/transformation_add_type_matrix.h"
27 #include "source/fuzz/transformation_add_type_pointer.h"
28 #include "source/fuzz/transformation_add_type_vector.h"
29 
30 namespace spvtools {
31 namespace fuzz {
32 
FuzzerPass(opt::IRContext * ir_context,FactManager * fact_manager,FuzzerContext * fuzzer_context,protobufs::TransformationSequence * transformations)33 FuzzerPass::FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager,
34                        FuzzerContext* fuzzer_context,
35                        protobufs::TransformationSequence* transformations)
36     : ir_context_(ir_context),
37       fact_manager_(fact_manager),
38       fuzzer_context_(fuzzer_context),
39       transformations_(transformations) {}
40 
41 FuzzerPass::~FuzzerPass() = default;
42 
FindAvailableInstructions(opt::Function * function,opt::BasicBlock * block,const opt::BasicBlock::iterator & inst_it,std::function<bool (opt::IRContext *,opt::Instruction *)> instruction_is_relevant) const43 std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
44     opt::Function* function, opt::BasicBlock* block,
45     const opt::BasicBlock::iterator& inst_it,
46     std::function<bool(opt::IRContext*, opt::Instruction*)>
47         instruction_is_relevant) const {
48   // TODO(afd) The following is (relatively) simple, but may end up being
49   //  prohibitively inefficient, as it walks the whole dominator tree for
50   //  every instruction that is considered.
51 
52   std::vector<opt::Instruction*> result;
53   // Consider all global declarations
54   for (auto& global : GetIRContext()->module()->types_values()) {
55     if (instruction_is_relevant(GetIRContext(), &global)) {
56       result.push_back(&global);
57     }
58   }
59 
60   // Consider all function parameters
61   function->ForEachParam(
62       [this, &instruction_is_relevant, &result](opt::Instruction* param) {
63         if (instruction_is_relevant(GetIRContext(), param)) {
64           result.push_back(param);
65         }
66       });
67 
68   // Consider all previous instructions in this block
69   for (auto prev_inst_it = block->begin(); prev_inst_it != inst_it;
70        ++prev_inst_it) {
71     if (instruction_is_relevant(GetIRContext(), &*prev_inst_it)) {
72       result.push_back(&*prev_inst_it);
73     }
74   }
75 
76   // Walk the dominator tree to consider all instructions from dominating
77   // blocks
78   auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
79   for (auto next_dominator = dominator_analysis->ImmediateDominator(block);
80        next_dominator != nullptr;
81        next_dominator =
82            dominator_analysis->ImmediateDominator(next_dominator)) {
83     for (auto& dominating_inst : *next_dominator) {
84       if (instruction_is_relevant(GetIRContext(), &dominating_inst)) {
85         result.push_back(&dominating_inst);
86       }
87     }
88   }
89   return result;
90 }
91 
MaybeAddTransformationBeforeEachInstruction(std::function<void (opt::Function * function,opt::BasicBlock * block,opt::BasicBlock::iterator inst_it,const protobufs::InstructionDescriptor & instruction_descriptor)> maybe_apply_transformation)92 void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
93     std::function<
94         void(opt::Function* function, opt::BasicBlock* block,
95              opt::BasicBlock::iterator inst_it,
96              const protobufs::InstructionDescriptor& instruction_descriptor)>
97         maybe_apply_transformation) {
98   // Consider every block in every function.
99   for (auto& function : *GetIRContext()->module()) {
100     for (auto& block : function) {
101       // We now consider every instruction in the block, randomly deciding
102       // whether to apply a transformation before it.
103 
104       // In order for transformations to insert new instructions, they need to
105       // be able to identify the instruction to insert before.  We describe an
106       // instruction via its opcode, 'opc', a base instruction 'base' that has a
107       // result id, and the number of instructions with opcode 'opc' that we
108       // should skip when searching from 'base' for the desired instruction.
109       // (An instruction that has a result id is represented by its own opcode,
110       // itself as 'base', and a skip-count of 0.)
111       std::vector<std::tuple<uint32_t, SpvOp, uint32_t>>
112           base_opcode_skip_triples;
113 
114       // The initial base instruction is the block label.
115       uint32_t base = block.id();
116 
117       // Counts the number of times we have seen each opcode since we reset the
118       // base instruction.
119       std::map<SpvOp, uint32_t> skip_count;
120 
121       // Consider every instruction in the block.  The label is excluded: it is
122       // only necessary to consider it as a base in case the first instruction
123       // in the block does not have a result id.
124       for (auto inst_it = block.begin(); inst_it != block.end(); ++inst_it) {
125         if (inst_it->HasResultId()) {
126           // In the case that the instruction has a result id, we use the
127           // instruction as its own base, and clear the skip counts we have
128           // collected.
129           base = inst_it->result_id();
130           skip_count.clear();
131         }
132         const SpvOp opcode = inst_it->opcode();
133 
134         // Invoke the provided function, which might apply a transformation.
135         maybe_apply_transformation(
136             &function, &block, inst_it,
137             MakeInstructionDescriptor(
138                 base, opcode,
139                 skip_count.count(opcode) ? skip_count.at(opcode) : 0));
140 
141         if (!inst_it->HasResultId()) {
142           skip_count[opcode] =
143               skip_count.count(opcode) ? skip_count.at(opcode) + 1 : 1;
144         }
145       }
146     }
147   }
148 }
149 
FindOrCreateBoolType()150 uint32_t FuzzerPass::FindOrCreateBoolType() {
151   opt::analysis::Bool bool_type;
152   auto existing_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
153   if (existing_id) {
154     return existing_id;
155   }
156   auto result = GetFuzzerContext()->GetFreshId();
157   ApplyTransformation(TransformationAddTypeBoolean(result));
158   return result;
159 }
160 
FindOrCreate32BitIntegerType(bool is_signed)161 uint32_t FuzzerPass::FindOrCreate32BitIntegerType(bool is_signed) {
162   opt::analysis::Integer int_type(32, is_signed);
163   auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
164   if (existing_id) {
165     return existing_id;
166   }
167   auto result = GetFuzzerContext()->GetFreshId();
168   ApplyTransformation(TransformationAddTypeInt(result, 32, is_signed));
169   return result;
170 }
171 
FindOrCreate32BitFloatType()172 uint32_t FuzzerPass::FindOrCreate32BitFloatType() {
173   opt::analysis::Float float_type(32);
174   auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
175   if (existing_id) {
176     return existing_id;
177   }
178   auto result = GetFuzzerContext()->GetFreshId();
179   ApplyTransformation(TransformationAddTypeFloat(result, 32));
180   return result;
181 }
182 
FindOrCreateVectorType(uint32_t component_type_id,uint32_t component_count)183 uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id,
184                                             uint32_t component_count) {
185   assert(component_count >= 2 && component_count <= 4 &&
186          "Precondition: component count must be in range [2, 4].");
187   opt::analysis::Type* component_type =
188       GetIRContext()->get_type_mgr()->GetType(component_type_id);
189   assert(component_type && "Precondition: the component type must exist.");
190   opt::analysis::Vector vector_type(component_type, component_count);
191   auto existing_id = GetIRContext()->get_type_mgr()->GetId(&vector_type);
192   if (existing_id) {
193     return existing_id;
194   }
195   auto result = GetFuzzerContext()->GetFreshId();
196   ApplyTransformation(
197       TransformationAddTypeVector(result, component_type_id, component_count));
198   return result;
199 }
200 
FindOrCreateMatrixType(uint32_t column_count,uint32_t row_count)201 uint32_t FuzzerPass::FindOrCreateMatrixType(uint32_t column_count,
202                                             uint32_t row_count) {
203   assert(column_count >= 2 && column_count <= 4 &&
204          "Precondition: column count must be in range [2, 4].");
205   assert(row_count >= 2 && row_count <= 4 &&
206          "Precondition: row count must be in range [2, 4].");
207   uint32_t column_type_id =
208       FindOrCreateVectorType(FindOrCreate32BitFloatType(), row_count);
209   opt::analysis::Type* column_type =
210       GetIRContext()->get_type_mgr()->GetType(column_type_id);
211   opt::analysis::Matrix matrix_type(column_type, column_count);
212   auto existing_id = GetIRContext()->get_type_mgr()->GetId(&matrix_type);
213   if (existing_id) {
214     return existing_id;
215   }
216   auto result = GetFuzzerContext()->GetFreshId();
217   ApplyTransformation(
218       TransformationAddTypeMatrix(result, column_type_id, column_count));
219   return result;
220 }
221 
FindOrCreatePointerType(uint32_t base_type_id,SpvStorageClass storage_class)222 uint32_t FuzzerPass::FindOrCreatePointerType(uint32_t base_type_id,
223                                              SpvStorageClass storage_class) {
224   // We do not use the type manager here, due to problems related to isomorphic
225   // but distinct structs not being regarded as different.
226   auto existing_id = fuzzerutil::MaybeGetPointerType(
227       GetIRContext(), base_type_id, storage_class);
228   if (existing_id) {
229     return existing_id;
230   }
231   auto result = GetFuzzerContext()->GetFreshId();
232   ApplyTransformation(
233       TransformationAddTypePointer(result, storage_class, base_type_id));
234   return result;
235 }
236 
FindOrCreatePointerTo32BitIntegerType(bool is_signed,SpvStorageClass storage_class)237 uint32_t FuzzerPass::FindOrCreatePointerTo32BitIntegerType(
238     bool is_signed, SpvStorageClass storage_class) {
239   return FindOrCreatePointerType(FindOrCreate32BitIntegerType(is_signed),
240                                  storage_class);
241 }
242 
FindOrCreate32BitIntegerConstant(uint32_t word,bool is_signed)243 uint32_t FuzzerPass::FindOrCreate32BitIntegerConstant(uint32_t word,
244                                                       bool is_signed) {
245   auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed);
246   opt::analysis::IntConstant int_constant(
247       GetIRContext()->get_type_mgr()->GetType(uint32_type_id)->AsInteger(),
248       {word});
249   auto existing_constant =
250       GetIRContext()->get_constant_mgr()->FindConstant(&int_constant);
251   if (existing_constant) {
252     return GetIRContext()
253         ->get_constant_mgr()
254         ->GetDefiningInstruction(existing_constant)
255         ->result_id();
256   }
257   auto result = GetFuzzerContext()->GetFreshId();
258   ApplyTransformation(
259       TransformationAddConstantScalar(result, uint32_type_id, {word}));
260   return result;
261 }
262 
FindOrCreate32BitFloatConstant(uint32_t word)263 uint32_t FuzzerPass::FindOrCreate32BitFloatConstant(uint32_t word) {
264   auto float_type_id = FindOrCreate32BitFloatType();
265   opt::analysis::FloatConstant float_constant(
266       GetIRContext()->get_type_mgr()->GetType(float_type_id)->AsFloat(),
267       {word});
268   auto existing_constant =
269       GetIRContext()->get_constant_mgr()->FindConstant(&float_constant);
270   if (existing_constant) {
271     return GetIRContext()
272         ->get_constant_mgr()
273         ->GetDefiningInstruction(existing_constant)
274         ->result_id();
275   }
276   auto result = GetFuzzerContext()->GetFreshId();
277   ApplyTransformation(
278       TransformationAddConstantScalar(result, float_type_id, {word}));
279   return result;
280 }
281 
FindOrCreateBoolConstant(bool value)282 uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value) {
283   auto bool_type_id = FindOrCreateBoolType();
284   opt::analysis::BoolConstant bool_constant(
285       GetIRContext()->get_type_mgr()->GetType(bool_type_id)->AsBool(), value);
286   auto existing_constant =
287       GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant);
288   if (existing_constant) {
289     return GetIRContext()
290         ->get_constant_mgr()
291         ->GetDefiningInstruction(existing_constant)
292         ->result_id();
293   }
294   auto result = GetFuzzerContext()->GetFreshId();
295   ApplyTransformation(TransformationAddConstantBoolean(result, value));
296   return result;
297 }
298 
FindOrCreateGlobalUndef(uint32_t type_id)299 uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
300   for (auto& inst : GetIRContext()->types_values()) {
301     if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
302       return inst.result_id();
303     }
304   }
305   auto result = GetFuzzerContext()->GetFreshId();
306   ApplyTransformation(TransformationAddGlobalUndef(result, type_id));
307   return result;
308 }
309 
310 std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
GetAvailableBaseTypesAndPointers(SpvStorageClass storage_class) const311 FuzzerPass::GetAvailableBaseTypesAndPointers(
312     SpvStorageClass storage_class) const {
313   // Records all of the base types available in the module.
314   std::vector<uint32_t> base_types;
315 
316   // For each base type, records all the associated pointer types that target
317   // that base type and that have |storage_class| as their storage class.
318   std::map<uint32_t, std::vector<uint32_t>> base_type_to_pointers;
319 
320   for (auto& inst : GetIRContext()->types_values()) {
321     switch (inst.opcode()) {
322       case SpvOpTypeArray:
323       case SpvOpTypeBool:
324       case SpvOpTypeFloat:
325       case SpvOpTypeInt:
326       case SpvOpTypeMatrix:
327       case SpvOpTypeStruct:
328       case SpvOpTypeVector:
329         // These types are suitable as pointer base types.  Record the type,
330         // and the fact that we cannot yet have seen any pointers that use this
331         // as its base type.
332         base_types.push_back(inst.result_id());
333         base_type_to_pointers.insert({inst.result_id(), {}});
334         break;
335       case SpvOpTypePointer:
336         if (inst.GetSingleWordInOperand(0) == storage_class) {
337           // The pointer has the desired storage class, so we are interested in
338           // it.  Associate it with its base type.
339           base_type_to_pointers.at(inst.GetSingleWordInOperand(1))
340               .push_back(inst.result_id());
341         }
342         break;
343       default:
344         break;
345     }
346   }
347   return {base_types, base_type_to_pointers};
348 }
349 
FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id)350 uint32_t FuzzerPass::FindOrCreateZeroConstant(
351     uint32_t scalar_or_composite_type_id) {
352   auto type_instruction =
353       GetIRContext()->get_def_use_mgr()->GetDef(scalar_or_composite_type_id);
354   assert(type_instruction && "The type instruction must exist.");
355   switch (type_instruction->opcode()) {
356     case SpvOpTypeBool:
357       return FindOrCreateBoolConstant(false);
358     case SpvOpTypeFloat:
359       return FindOrCreate32BitFloatConstant(0);
360     case SpvOpTypeInt:
361       return FindOrCreate32BitIntegerConstant(
362           0, type_instruction->GetSingleWordInOperand(1) != 0);
363     case SpvOpTypeArray: {
364       return GetZeroConstantForHomogeneousComposite(
365           *type_instruction, type_instruction->GetSingleWordInOperand(0),
366           fuzzerutil::GetArraySize(*type_instruction, GetIRContext()));
367     }
368     case SpvOpTypeMatrix:
369     case SpvOpTypeVector: {
370       return GetZeroConstantForHomogeneousComposite(
371           *type_instruction, type_instruction->GetSingleWordInOperand(0),
372           type_instruction->GetSingleWordInOperand(1));
373     }
374     case SpvOpTypeStruct: {
375       std::vector<const opt::analysis::Constant*> field_zero_constants;
376       std::vector<uint32_t> field_zero_ids;
377       for (uint32_t index = 0; index < type_instruction->NumInOperands();
378            index++) {
379         uint32_t field_constant_id = FindOrCreateZeroConstant(
380             type_instruction->GetSingleWordInOperand(index));
381         field_zero_ids.push_back(field_constant_id);
382         field_zero_constants.push_back(
383             GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
384                 field_constant_id));
385       }
386       return FindOrCreateCompositeConstant(
387           *type_instruction, field_zero_constants, field_zero_ids);
388     }
389     default:
390       assert(false && "Unknown type.");
391       return 0;
392   }
393 }
394 
FindOrCreateCompositeConstant(const opt::Instruction & composite_type_instruction,const std::vector<const opt::analysis::Constant * > & constants,const std::vector<uint32_t> & constant_ids)395 uint32_t FuzzerPass::FindOrCreateCompositeConstant(
396     const opt::Instruction& composite_type_instruction,
397     const std::vector<const opt::analysis::Constant*>& constants,
398     const std::vector<uint32_t>& constant_ids) {
399   assert(constants.size() == constant_ids.size() &&
400          "Precondition: |constants| and |constant_ids| must be in "
401          "correspondence.");
402 
403   opt::analysis::Type* composite_type = GetIRContext()->get_type_mgr()->GetType(
404       composite_type_instruction.result_id());
405   std::unique_ptr<opt::analysis::Constant> composite_constant;
406   if (composite_type->AsArray()) {
407     composite_constant = MakeUnique<opt::analysis::ArrayConstant>(
408         composite_type->AsArray(), constants);
409   } else if (composite_type->AsMatrix()) {
410     composite_constant = MakeUnique<opt::analysis::MatrixConstant>(
411         composite_type->AsMatrix(), constants);
412   } else if (composite_type->AsStruct()) {
413     composite_constant = MakeUnique<opt::analysis::StructConstant>(
414         composite_type->AsStruct(), constants);
415   } else if (composite_type->AsVector()) {
416     composite_constant = MakeUnique<opt::analysis::VectorConstant>(
417         composite_type->AsVector(), constants);
418   } else {
419     assert(false &&
420            "Precondition: |composite_type| must declare a composite type.");
421     return 0;
422   }
423 
424   uint32_t existing_constant =
425       GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
426           composite_constant.get(), composite_type_instruction.result_id());
427   if (existing_constant) {
428     return existing_constant;
429   }
430   uint32_t result = GetFuzzerContext()->GetFreshId();
431   ApplyTransformation(TransformationAddConstantComposite(
432       result, composite_type_instruction.result_id(), constant_ids));
433   return result;
434 }
435 
GetZeroConstantForHomogeneousComposite(const opt::Instruction & composite_type_instruction,uint32_t component_type_id,uint32_t num_components)436 uint32_t FuzzerPass::GetZeroConstantForHomogeneousComposite(
437     const opt::Instruction& composite_type_instruction,
438     uint32_t component_type_id, uint32_t num_components) {
439   std::vector<const opt::analysis::Constant*> zero_constants;
440   std::vector<uint32_t> zero_ids;
441   uint32_t zero_component = FindOrCreateZeroConstant(component_type_id);
442   const opt::analysis::Constant* registered_zero_component =
443       GetIRContext()->get_constant_mgr()->FindDeclaredConstant(zero_component);
444   for (uint32_t i = 0; i < num_components; i++) {
445     zero_constants.push_back(registered_zero_component);
446     zero_ids.push_back(zero_component);
447   }
448   return FindOrCreateCompositeConstant(composite_type_instruction,
449                                        zero_constants, zero_ids);
450 }
451 
452 }  // namespace fuzz
453 }  // namespace spvtools
454