fuzzer_pass_apply_id_synonyms.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // Copyright (c) 2019 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_apply_id_synonyms.h"
  15. #include "source/fuzz/data_descriptor.h"
  16. #include "source/fuzz/fuzzer_util.h"
  17. #include "source/fuzz/id_use_descriptor.h"
  18. #include "source/fuzz/instruction_descriptor.h"
  19. #include "source/fuzz/transformation_composite_extract.h"
  20. #include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
  21. #include "source/fuzz/transformation_replace_id_with_synonym.h"
  22. namespace spvtools {
  23. namespace fuzz {
  24. FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms(
  25. opt::IRContext* ir_context, TransformationContext* transformation_context,
  26. FuzzerContext* fuzzer_context,
  27. protobufs::TransformationSequence* transformations)
  28. : FuzzerPass(ir_context, transformation_context, fuzzer_context,
  29. transformations) {}
  30. FuzzerPassApplyIdSynonyms::~FuzzerPassApplyIdSynonyms() = default;
  31. void FuzzerPassApplyIdSynonyms::Apply() {
  32. // Compute a closure of data synonym facts, to enrich the pool of synonyms
  33. // that are available.
  34. ApplyTransformation(TransformationComputeDataSynonymFactClosure(
  35. GetFuzzerContext()
  36. ->GetMaximumEquivalenceClassSizeForDataSynonymFactClosure()));
  37. for (auto id_with_known_synonyms : GetTransformationContext()
  38. ->GetFactManager()
  39. ->GetIdsForWhichSynonymsAreKnown()) {
  40. // Gather up all uses of |id_with_known_synonym| as a regular id, and
  41. // subsequently iterate over these uses. We use this separation because,
  42. // when considering a given use, we might apply a transformation that will
  43. // invalidate the def-use manager.
  44. std::vector<std::pair<opt::Instruction*, uint32_t>> uses;
  45. GetIRContext()->get_def_use_mgr()->ForEachUse(
  46. id_with_known_synonyms,
  47. [&uses](opt::Instruction* use_inst, uint32_t use_index) -> void {
  48. // We only gather up regular id uses; e.g. we do not include a use of
  49. // the id as the scope for an atomic operation.
  50. if (use_inst->GetOperand(use_index).type == SPV_OPERAND_TYPE_ID) {
  51. uses.emplace_back(
  52. std::pair<opt::Instruction*, uint32_t>(use_inst, use_index));
  53. }
  54. });
  55. for (auto& use : uses) {
  56. auto use_inst = use.first;
  57. auto use_index = use.second;
  58. auto block_containing_use = GetIRContext()->get_instr_block(use_inst);
  59. // The use might not be in a block; e.g. it could be a decoration.
  60. if (!block_containing_use) {
  61. continue;
  62. }
  63. if (!GetFuzzerContext()->ChoosePercentage(
  64. GetFuzzerContext()->GetChanceOfReplacingIdWithSynonym())) {
  65. continue;
  66. }
  67. // |use_index| is the absolute index of the operand. We require
  68. // the index of the operand restricted to input operands only, so
  69. // we subtract the number of non-input operands from |use_index|.
  70. uint32_t use_in_operand_index =
  71. use_index - use_inst->NumOperands() + use_inst->NumInOperands();
  72. if (!TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
  73. GetIRContext(), use_inst, use_in_operand_index)) {
  74. continue;
  75. }
  76. std::vector<const protobufs::DataDescriptor*> synonyms_to_try;
  77. for (auto& data_descriptor :
  78. GetTransformationContext()->GetFactManager()->GetSynonymsForId(
  79. id_with_known_synonyms)) {
  80. protobufs::DataDescriptor descriptor_for_this_id =
  81. MakeDataDescriptor(id_with_known_synonyms, {});
  82. if (DataDescriptorEquals()(data_descriptor, &descriptor_for_this_id)) {
  83. // Exclude the fact that the id is synonymous with itself.
  84. continue;
  85. }
  86. synonyms_to_try.push_back(data_descriptor);
  87. }
  88. while (!synonyms_to_try.empty()) {
  89. auto synonym_to_try =
  90. GetFuzzerContext()->RemoveAtRandomIndex(&synonyms_to_try);
  91. // If the synonym's |index_size| is zero, the synonym represents an id.
  92. // Otherwise it represents some element of a composite structure, in
  93. // which case we need to be able to add an extract instruction to get
  94. // that element out.
  95. if (synonym_to_try->index_size() > 0 &&
  96. !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
  97. use_inst) &&
  98. use_inst->opcode() != SpvOpPhi) {
  99. // We cannot insert an extract before this instruction, so this
  100. // synonym is no good.
  101. continue;
  102. }
  103. if (!fuzzerutil::IdIsAvailableAtUse(GetIRContext(), use_inst,
  104. use_in_operand_index,
  105. synonym_to_try->object())) {
  106. continue;
  107. }
  108. // We either replace the use with an id known to be synonymous (when
  109. // the synonym's |index_size| is 0), or an id that will hold the result
  110. // of extracting a synonym from a composite (when the synonym's
  111. // |index_size| is > 0).
  112. uint32_t id_with_which_to_replace_use;
  113. if (synonym_to_try->index_size() == 0) {
  114. id_with_which_to_replace_use = synonym_to_try->object();
  115. } else {
  116. id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId();
  117. opt::Instruction* instruction_to_insert_before = nullptr;
  118. if (use_inst->opcode() != SpvOpPhi) {
  119. instruction_to_insert_before = use_inst;
  120. } else {
  121. auto parent_block_id =
  122. use_inst->GetSingleWordInOperand(use_in_operand_index + 1);
  123. auto parent_block_instruction =
  124. GetIRContext()->get_def_use_mgr()->GetDef(parent_block_id);
  125. auto parent_block =
  126. GetIRContext()->get_instr_block(parent_block_instruction);
  127. instruction_to_insert_before = parent_block->GetMergeInst()
  128. ? parent_block->GetMergeInst()
  129. : parent_block->terminator();
  130. }
  131. ApplyTransformation(TransformationCompositeExtract(
  132. MakeInstructionDescriptor(GetIRContext(),
  133. instruction_to_insert_before),
  134. id_with_which_to_replace_use, synonym_to_try->object(),
  135. fuzzerutil::RepeatedFieldToVector(synonym_to_try->index())));
  136. }
  137. ApplyTransformation(TransformationReplaceIdWithSynonym(
  138. MakeIdUseDescriptorFromUse(GetIRContext(), use_inst,
  139. use_in_operand_index),
  140. id_with_which_to_replace_use));
  141. break;
  142. }
  143. }
  144. }
  145. }
  146. } // namespace fuzz
  147. } // namespace spvtools