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