fuzzer_pass_add_access_chains.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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, FactManager* fact_manager,
  21. FuzzerContext* fuzzer_context,
  22. protobufs::TransformationSequence* transformations)
  23. : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
  24. FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default;
  25. void FuzzerPassAddAccessChains::Apply() {
  26. ForEachInstructionWithInstructionDescriptor(
  27. [this](opt::Function* function, opt::BasicBlock* block,
  28. opt::BasicBlock::iterator inst_it,
  29. const protobufs::InstructionDescriptor& instruction_descriptor)
  30. -> void {
  31. assert(inst_it->opcode() ==
  32. instruction_descriptor.target_instruction_opcode() &&
  33. "The opcode of the instruction we might insert before must be "
  34. "the same as the opcode in the descriptor for the instruction");
  35. // Check whether it is legitimate to insert an access chain
  36. // instruction before this instruction.
  37. if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAccessChain,
  38. inst_it)) {
  39. return;
  40. }
  41. // Randomly decide whether to try inserting a load here.
  42. if (!GetFuzzerContext()->ChoosePercentage(
  43. GetFuzzerContext()->GetChanceOfAddingAccessChain())) {
  44. return;
  45. }
  46. // Get all of the pointers that are currently in scope, excluding
  47. // explicitly null and undefined pointers.
  48. std::vector<opt::Instruction*> relevant_pointer_instructions =
  49. FindAvailableInstructions(
  50. function, block, inst_it,
  51. [](opt::IRContext* context,
  52. opt::Instruction* instruction) -> bool {
  53. if (!instruction->result_id() || !instruction->type_id()) {
  54. // A pointer needs both a result and type id.
  55. return false;
  56. }
  57. switch (instruction->opcode()) {
  58. case SpvOpConstantNull:
  59. case SpvOpUndef:
  60. // Do not allow making an access chain from a null or
  61. // undefined pointer. (We can eliminate these cases
  62. // before actually checking that the instruction is a
  63. // pointer.)
  64. return false;
  65. default:
  66. break;
  67. }
  68. // If the instruction has pointer type, we can legitimately
  69. // make an access chain from it.
  70. return context->get_def_use_mgr()
  71. ->GetDef(instruction->type_id())
  72. ->opcode() == SpvOpTypePointer;
  73. });
  74. // At this point, |relevant_instructions| contains all the pointers
  75. // we might think of making an access chain from.
  76. if (relevant_pointer_instructions.empty()) {
  77. return;
  78. }
  79. auto chosen_pointer =
  80. relevant_pointer_instructions[GetFuzzerContext()->RandomIndex(
  81. relevant_pointer_instructions)];
  82. std::vector<uint32_t> index_ids;
  83. auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef(
  84. chosen_pointer->type_id());
  85. uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
  86. while (true) {
  87. auto subobject_type =
  88. GetIRContext()->get_def_use_mgr()->GetDef(subobject_type_id);
  89. if (!spvOpcodeIsComposite(subobject_type->opcode())) {
  90. break;
  91. }
  92. if (!GetFuzzerContext()->ChoosePercentage(
  93. GetFuzzerContext()
  94. ->GetChanceOfGoingDeeperWhenMakingAccessChain())) {
  95. break;
  96. }
  97. uint32_t bound;
  98. switch (subobject_type->opcode()) {
  99. case SpvOpTypeArray:
  100. bound = fuzzerutil::GetArraySize(*subobject_type, GetIRContext());
  101. break;
  102. case SpvOpTypeMatrix:
  103. case SpvOpTypeVector:
  104. bound = subobject_type->GetSingleWordInOperand(1);
  105. break;
  106. case SpvOpTypeStruct:
  107. bound = fuzzerutil::GetNumberOfStructMembers(*subobject_type);
  108. break;
  109. default:
  110. assert(false && "Not a composite type opcode.");
  111. // Set the bound to a value in order to keep release compilers
  112. // happy.
  113. bound = 0;
  114. break;
  115. }
  116. if (bound == 0) {
  117. // It is possible for a composite type to legitimately have zero
  118. // sub-components, at least in the case of a struct, which
  119. // can have no fields.
  120. break;
  121. }
  122. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We
  123. // could allow non-constant indices when looking up non-structs,
  124. // using clamping to ensure they are in-bounds.
  125. uint32_t index_value =
  126. GetFuzzerContext()->GetRandomIndexForAccessChain(bound);
  127. index_ids.push_back(FindOrCreate32BitIntegerConstant(
  128. index_value, GetFuzzerContext()->ChooseEven()));
  129. switch (subobject_type->opcode()) {
  130. case SpvOpTypeArray:
  131. case SpvOpTypeMatrix:
  132. case SpvOpTypeVector:
  133. subobject_type_id = subobject_type->GetSingleWordInOperand(0);
  134. break;
  135. case SpvOpTypeStruct:
  136. subobject_type_id =
  137. subobject_type->GetSingleWordInOperand(index_value);
  138. break;
  139. default:
  140. assert(false && "Not a composite type opcode.");
  141. }
  142. }
  143. // The transformation we are about to create will only apply if a
  144. // pointer suitable for the access chain's result type exists, so we
  145. // create one if it does not.
  146. FindOrCreatePointerType(subobject_type_id,
  147. static_cast<SpvStorageClass>(
  148. pointer_type->GetSingleWordInOperand(0)));
  149. // Apply the transformation to add an access chain.
  150. ApplyTransformation(TransformationAccessChain(
  151. GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(),
  152. index_ids, instruction_descriptor));
  153. });
  154. }
  155. } // namespace fuzz
  156. } // namespace spvtools