fuzzer_pass_add_access_chains.cpp 7.1 KB

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