• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2020 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_add_composite_inserts.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19 #include "source/fuzz/pseudo_random_generator.h"
20 #include "source/fuzz/transformation_composite_insert.h"
21 
22 namespace spvtools {
23 namespace fuzz {
24 
FuzzerPassAddCompositeInserts(opt::IRContext * ir_context,TransformationContext * transformation_context,FuzzerContext * fuzzer_context,protobufs::TransformationSequence * transformations)25 FuzzerPassAddCompositeInserts::FuzzerPassAddCompositeInserts(
26     opt::IRContext* ir_context, TransformationContext* transformation_context,
27     FuzzerContext* fuzzer_context,
28     protobufs::TransformationSequence* transformations)
29     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
30                  transformations) {}
31 
Apply()32 void FuzzerPassAddCompositeInserts::Apply() {
33   ForEachInstructionWithInstructionDescriptor(
34       [this](opt::Function* function, opt::BasicBlock* block,
35              opt::BasicBlock::iterator instruction_iterator,
36              const protobufs::InstructionDescriptor& instruction_descriptor)
37           -> void {
38         assert(instruction_iterator->opcode() ==
39                    instruction_descriptor.target_instruction_opcode() &&
40                "The opcode of the instruction we might insert before must be "
41                "the same as the opcode in the descriptor for the instruction");
42 
43         // Randomly decide whether to try adding an OpCompositeInsert
44         // instruction.
45         if (!GetFuzzerContext()->ChoosePercentage(
46                 GetFuzzerContext()->GetChanceOfAddingCompositeInsert())) {
47           return;
48         }
49 
50         // It must be possible to insert an OpCompositeInsert instruction
51         // before |instruction_iterator|.
52         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
53                 SpvOpCompositeInsert, instruction_iterator)) {
54           return;
55         }
56 
57         // Look for available values that have composite type.
58         std::vector<opt::Instruction*> available_composites =
59             FindAvailableInstructions(
60                 function, block, instruction_iterator,
61                 [instruction_descriptor](
62                     opt::IRContext* ir_context,
63                     opt::Instruction* instruction) -> bool {
64                   // |instruction| must be a supported instruction of composite
65                   // type.
66                   if (!TransformationCompositeInsert::
67                           IsCompositeInstructionSupported(ir_context,
68                                                           instruction)) {
69                     return false;
70                   }
71 
72                   auto instruction_type = ir_context->get_type_mgr()->GetType(
73                       instruction->type_id());
74 
75                   // No components of the composite can have type
76                   // OpTypeRuntimeArray.
77                   if (ContainsRuntimeArray(*instruction_type)) {
78                     return false;
79                   }
80 
81                   // No components of the composite can be pointers.
82                   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3658):
83                   //       Structs can have components of pointer type.
84                   //       FindOrCreateZeroConstant cannot be called on a
85                   //       pointer. We ignore pointers for now. Consider adding
86                   //       support for pointer types.
87                   if (ContainsPointer(*instruction_type)) {
88                     return false;
89                   }
90 
91                   return true;
92                 });
93 
94         // If there are no available values, then return.
95         if (available_composites.empty()) {
96           return;
97         }
98 
99         // Choose randomly one available composite value.
100         auto available_composite =
101             available_composites[GetFuzzerContext()->RandomIndex(
102                 available_composites)];
103 
104         // Take a random component of the chosen composite value. If the chosen
105         // component is itself a composite, then randomly decide whether to take
106         // its component and repeat.
107         uint32_t current_node_type_id = available_composite->type_id();
108         std::vector<uint32_t> path_to_replaced;
109         while (true) {
110           auto current_node_type_inst =
111               GetIRContext()->get_def_use_mgr()->GetDef(current_node_type_id);
112           uint32_t num_of_components = fuzzerutil::GetBoundForCompositeIndex(
113               *current_node_type_inst, GetIRContext());
114 
115           // If the composite is empty, then end the iteration.
116           if (num_of_components == 0) {
117             break;
118           }
119           uint32_t one_selected_index =
120               GetFuzzerContext()->GetRandomIndexForCompositeInsert(
121                   num_of_components);
122 
123           // Construct a final index by appending the current index.
124           path_to_replaced.push_back(one_selected_index);
125           current_node_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
126               GetIRContext(), current_node_type_id, one_selected_index);
127 
128           // If the component is not a composite then end the iteration.
129           if (!fuzzerutil::IsCompositeType(
130                   GetIRContext()->get_type_mgr()->GetType(
131                       current_node_type_id))) {
132             break;
133           }
134 
135           // If the component is a composite, but we decide not to go deeper,
136           // then end the iteration.
137           if (!GetFuzzerContext()->ChoosePercentage(
138                   GetFuzzerContext()
139                       ->GetChanceOfGoingDeeperToInsertInComposite())) {
140             break;
141           }
142         }
143 
144         // Look for available objects that have the type id
145         // |current_node_type_id| and can be inserted.
146         std::vector<opt::Instruction*> available_objects =
147             FindAvailableInstructions(
148                 function, block, instruction_iterator,
149                 [instruction_descriptor, current_node_type_id](
150                     opt::IRContext* /*unused*/,
151                     opt::Instruction* instruction) -> bool {
152                   if (instruction->result_id() == 0 ||
153                       instruction->type_id() == 0) {
154                     return false;
155                   }
156                   if (instruction->type_id() != current_node_type_id) {
157                     return false;
158                   }
159                   return true;
160                 });
161 
162         // If there are no objects of the specific type available, check if
163         // FindOrCreateZeroConstant can be called and create a zero constant of
164         // this type.
165         uint32_t available_object_id;
166         if (available_objects.empty()) {
167           if (!fuzzerutil::CanCreateConstant(GetIRContext(),
168                                              current_node_type_id)) {
169             return;
170           }
171           available_object_id =
172               FindOrCreateZeroConstant(current_node_type_id, false);
173         } else {
174           available_object_id =
175               available_objects[GetFuzzerContext()->RandomIndex(
176                                     available_objects)]
177                   ->result_id();
178         }
179         auto new_result_id = GetFuzzerContext()->GetFreshId();
180 
181         // Insert an OpCompositeInsert instruction which copies
182         // |available_composite| and in the copy inserts the object
183         // of type |available_object_id| at index |index_to_replace|.
184         ApplyTransformation(TransformationCompositeInsert(
185             instruction_descriptor, new_result_id,
186             available_composite->result_id(), available_object_id,
187             path_to_replaced));
188       });
189 }
190 
ContainsPointer(const opt::analysis::Type & type)191 bool FuzzerPassAddCompositeInserts::ContainsPointer(
192     const opt::analysis::Type& type) {
193   switch (type.kind()) {
194     case opt::analysis::Type::kPointer:
195       return true;
196     case opt::analysis::Type::kArray:
197       return ContainsPointer(*type.AsArray()->element_type());
198     case opt::analysis::Type::kMatrix:
199       return ContainsPointer(*type.AsMatrix()->element_type());
200     case opt::analysis::Type::kVector:
201       return ContainsPointer(*type.AsVector()->element_type());
202     case opt::analysis::Type::kStruct:
203       return std::any_of(type.AsStruct()->element_types().begin(),
204                          type.AsStruct()->element_types().end(),
205                          [](const opt::analysis::Type* element_type) {
206                            return ContainsPointer(*element_type);
207                          });
208     default:
209       return false;
210   }
211 }
212 
ContainsRuntimeArray(const opt::analysis::Type & type)213 bool FuzzerPassAddCompositeInserts::ContainsRuntimeArray(
214     const opt::analysis::Type& type) {
215   switch (type.kind()) {
216     case opt::analysis::Type::kRuntimeArray:
217       return true;
218     case opt::analysis::Type::kStruct:
219       // If any component of a struct is of type OpTypeRuntimeArray, return
220       // true.
221       return std::any_of(type.AsStruct()->element_types().begin(),
222                          type.AsStruct()->element_types().end(),
223                          [](const opt::analysis::Type* element_type) {
224                            return ContainsRuntimeArray(*element_type);
225                          });
226     default:
227       return false;
228   }
229 }
230 
231 }  // namespace fuzz
232 }  // namespace spvtools
233