fuzzer_pass_add_access_chains.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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_access_chains.h"
  15. #include "source/fuzz/fuzzer_util.h"
  16. #include "source/fuzz/transformation_access_chain.h"
  17. namespace spvtools {
  18. namespace fuzz {
  19. FuzzerPassAddAccessChains::FuzzerPassAddAccessChains(
  20. opt::IRContext* ir_context, TransformationContext* transformation_context,
  21. FuzzerContext* fuzzer_context,
  22. protobufs::TransformationSequence* transformations,
  23. bool ignore_inapplicable_transformations)
  24. : FuzzerPass(ir_context, transformation_context, fuzzer_context,
  25. transformations, ignore_inapplicable_transformations) {}
  26. void FuzzerPassAddAccessChains::Apply() {
  27. ForEachInstructionWithInstructionDescriptor(
  28. [this](opt::Function* function, opt::BasicBlock* block,
  29. opt::BasicBlock::iterator inst_it,
  30. const protobufs::InstructionDescriptor& instruction_descriptor)
  31. -> void {
  32. assert(
  33. inst_it->opcode() ==
  34. spv::Op(instruction_descriptor.target_instruction_opcode()) &&
  35. "The opcode of the instruction we might insert before must be "
  36. "the same as the opcode in the descriptor for the instruction");
  37. // Check whether it is legitimate to insert an access chain
  38. // instruction before this instruction.
  39. if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
  40. spv::Op::OpAccessChain, inst_it)) {
  41. return;
  42. }
  43. // Randomly decide whether to try inserting a load here.
  44. if (!GetFuzzerContext()->ChoosePercentage(
  45. GetFuzzerContext()->GetChanceOfAddingAccessChain())) {
  46. return;
  47. }
  48. // Get all of the pointers that are currently in scope, excluding
  49. // explicitly null and undefined pointers.
  50. std::vector<opt::Instruction*> relevant_pointer_instructions =
  51. FindAvailableInstructions(
  52. function, block, inst_it,
  53. [](opt::IRContext* context,
  54. opt::Instruction* instruction) -> bool {
  55. if (!instruction->result_id() || !instruction->type_id()) {
  56. // A pointer needs both a result and type id.
  57. return false;
  58. }
  59. switch (instruction->opcode()) {
  60. case spv::Op::OpConstantNull:
  61. case spv::Op::OpUndef:
  62. // Do not allow making an access chain from a null or
  63. // undefined pointer. (We can eliminate these cases
  64. // before actually checking that the instruction is a
  65. // pointer.)
  66. return false;
  67. default:
  68. break;
  69. }
  70. // If the instruction has pointer type, we can legitimately
  71. // make an access chain from it.
  72. return context->get_def_use_mgr()
  73. ->GetDef(instruction->type_id())
  74. ->opcode() == spv::Op::OpTypePointer;
  75. });
  76. // At this point, |relevant_instructions| contains all the pointers
  77. // we might think of making an access chain from.
  78. if (relevant_pointer_instructions.empty()) {
  79. return;
  80. }
  81. auto chosen_pointer =
  82. relevant_pointer_instructions[GetFuzzerContext()->RandomIndex(
  83. relevant_pointer_instructions)];
  84. std::vector<uint32_t> index_ids;
  85. // Each index accessing a non-struct composite will be clamped, thus
  86. // needing a pair of fresh ids
  87. std::vector<std::pair<uint32_t, uint32_t>> fresh_ids_for_clamping;
  88. auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef(
  89. chosen_pointer->type_id());
  90. uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
  91. while (true) {
  92. auto subobject_type =
  93. GetIRContext()->get_def_use_mgr()->GetDef(subobject_type_id);
  94. if (!spvOpcodeIsComposite(subobject_type->opcode())) {
  95. break;
  96. }
  97. if (!GetFuzzerContext()->ChoosePercentage(
  98. GetFuzzerContext()
  99. ->GetChanceOfGoingDeeperWhenMakingAccessChain())) {
  100. break;
  101. }
  102. uint32_t bound;
  103. switch (subobject_type->opcode()) {
  104. case spv::Op::OpTypeArray:
  105. bound = fuzzerutil::GetArraySize(*subobject_type, GetIRContext());
  106. break;
  107. case spv::Op::OpTypeMatrix:
  108. case spv::Op::OpTypeVector:
  109. bound = subobject_type->GetSingleWordInOperand(1);
  110. break;
  111. case spv::Op::OpTypeStruct:
  112. bound = fuzzerutil::GetNumberOfStructMembers(*subobject_type);
  113. break;
  114. default:
  115. assert(false && "Not a composite type opcode.");
  116. // Set the bound to a value in order to keep release compilers
  117. // happy.
  118. bound = 0;
  119. break;
  120. }
  121. if (bound == 0) {
  122. // It is possible for a composite type to legitimately have zero
  123. // sub-components, at least in the case of a struct, which
  124. // can have no fields.
  125. break;
  126. }
  127. uint32_t index_value =
  128. GetFuzzerContext()->GetRandomIndexForAccessChain(bound);
  129. switch (subobject_type->opcode()) {
  130. case spv::Op::OpTypeArray:
  131. case spv::Op::OpTypeMatrix:
  132. case spv::Op::OpTypeVector: {
  133. // The index will be clamped
  134. bool is_signed = GetFuzzerContext()->ChooseEven();
  135. // Make the constant ready for clamping. We need:
  136. // - an OpTypeBool to be present in the module
  137. // - an OpConstant with the same type as the index and value
  138. // the maximum value for an index
  139. // - a new pair of fresh ids for the clamping instructions
  140. FindOrCreateBoolType();
  141. FindOrCreateIntegerConstant({bound - 1}, 32, is_signed, false);
  142. std::pair<uint32_t, uint32_t> fresh_pair_of_ids = {
  143. GetFuzzerContext()->GetFreshId(),
  144. GetFuzzerContext()->GetFreshId()};
  145. fresh_ids_for_clamping.emplace_back(fresh_pair_of_ids);
  146. index_ids.push_back(FindOrCreateIntegerConstant(
  147. {index_value}, 32, is_signed, false));
  148. subobject_type_id = subobject_type->GetSingleWordInOperand(0);
  149. } break;
  150. case spv::Op::OpTypeStruct:
  151. index_ids.push_back(FindOrCreateIntegerConstant(
  152. {index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
  153. subobject_type_id =
  154. subobject_type->GetSingleWordInOperand(index_value);
  155. break;
  156. default:
  157. assert(false && "Not a composite type opcode.");
  158. }
  159. }
  160. // The transformation we are about to create will only apply if a
  161. // pointer suitable for the access chain's result type exists, so we
  162. // create one if it does not.
  163. FindOrCreatePointerType(subobject_type_id,
  164. static_cast<spv::StorageClass>(
  165. pointer_type->GetSingleWordInOperand(0)));
  166. // Apply the transformation to add an access chain.
  167. ApplyTransformation(TransformationAccessChain(
  168. GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(),
  169. index_ids, instruction_descriptor, fresh_ids_for_clamping));
  170. });
  171. }
  172. } // namespace fuzz
  173. } // namespace spvtools