fuzzer_pass_add_composite_inserts.cpp 9.5 KB

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