transformation_store.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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/transformation_store.h"
  15. #include "source/fuzz/fuzzer_util.h"
  16. #include "source/fuzz/instruction_descriptor.h"
  17. namespace spvtools {
  18. namespace fuzz {
  19. TransformationStore::TransformationStore(protobufs::TransformationStore message)
  20. : message_(std::move(message)) {}
  21. TransformationStore::TransformationStore(
  22. uint32_t pointer_id, bool is_atomic, uint32_t memory_scope,
  23. uint32_t memory_semantics, uint32_t value_id,
  24. const protobufs::InstructionDescriptor& instruction_to_insert_before) {
  25. message_.set_pointer_id(pointer_id);
  26. message_.set_is_atomic(is_atomic);
  27. message_.set_memory_scope_id(memory_scope);
  28. message_.set_memory_semantics_id(memory_semantics);
  29. message_.set_value_id(value_id);
  30. *message_.mutable_instruction_to_insert_before() =
  31. instruction_to_insert_before;
  32. }
  33. bool TransformationStore::IsApplicable(
  34. opt::IRContext* ir_context,
  35. const TransformationContext& transformation_context) const {
  36. // The pointer must exist and have a type.
  37. auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
  38. if (!pointer || !pointer->type_id()) {
  39. return false;
  40. }
  41. // The pointer type must indeed be a pointer.
  42. auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
  43. assert(pointer_type && "Type id must be defined.");
  44. if (pointer_type->opcode() != spv::Op::OpTypePointer) {
  45. return false;
  46. }
  47. // The pointer must not be read only.
  48. if (pointer->IsReadOnlyPointer()) {
  49. return false;
  50. }
  51. // We do not want to allow storing to null or undefined pointers.
  52. switch (pointer->opcode()) {
  53. case spv::Op::OpConstantNull:
  54. case spv::Op::OpUndef:
  55. return false;
  56. default:
  57. break;
  58. }
  59. // Determine which instruction we should be inserting before.
  60. auto insert_before =
  61. FindInstruction(message_.instruction_to_insert_before(), ir_context);
  62. // It must exist, ...
  63. if (!insert_before) {
  64. return false;
  65. }
  66. // ... and it must be legitimate to insert a store before it.
  67. if (!message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
  68. spv::Op::OpStore, insert_before)) {
  69. return false;
  70. }
  71. if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
  72. spv::Op::OpAtomicStore, insert_before)) {
  73. return false;
  74. }
  75. // The block we are inserting into needs to be dead, or else the pointee type
  76. // of the pointer we are storing to needs to be irrelevant (otherwise the
  77. // store could impact on the observable behaviour of the module).
  78. if (!transformation_context.GetFactManager()->BlockIsDead(
  79. ir_context->get_instr_block(insert_before)->id()) &&
  80. !transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
  81. message_.pointer_id())) {
  82. return false;
  83. }
  84. // The value being stored needs to exist and have a type.
  85. auto value = ir_context->get_def_use_mgr()->GetDef(message_.value_id());
  86. if (!value || !value->type_id()) {
  87. return false;
  88. }
  89. // The type of the value must match the pointee type.
  90. if (pointer_type->GetSingleWordInOperand(1) != value->type_id()) {
  91. return false;
  92. }
  93. // The pointer needs to be available at the insertion point.
  94. if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
  95. message_.pointer_id())) {
  96. return false;
  97. }
  98. if (message_.is_atomic()) {
  99. // Check the exists of memory scope and memory semantics ids.
  100. auto memory_scope_instruction =
  101. ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id());
  102. auto memory_semantics_instruction =
  103. ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id());
  104. if (!memory_scope_instruction) {
  105. return false;
  106. }
  107. if (!memory_semantics_instruction) {
  108. return false;
  109. }
  110. // The memory scope and memory semantics instructions must have the
  111. // 'OpConstant' opcode.
  112. if (memory_scope_instruction->opcode() != spv::Op::OpConstant) {
  113. return false;
  114. }
  115. if (memory_semantics_instruction->opcode() != spv::Op::OpConstant) {
  116. return false;
  117. }
  118. // The memory scope and memory semantics need to be available before
  119. // |insert_before|.
  120. if (!fuzzerutil::IdIsAvailableBeforeInstruction(
  121. ir_context, insert_before, message_.memory_scope_id())) {
  122. return false;
  123. }
  124. if (!fuzzerutil::IdIsAvailableBeforeInstruction(
  125. ir_context, insert_before, message_.memory_semantics_id())) {
  126. return false;
  127. }
  128. // The memory scope and memory semantics instructions must have an Integer
  129. // operand type with signedness does not matters.
  130. if (ir_context->get_def_use_mgr()
  131. ->GetDef(memory_scope_instruction->type_id())
  132. ->opcode() != spv::Op::OpTypeInt) {
  133. return false;
  134. }
  135. if (ir_context->get_def_use_mgr()
  136. ->GetDef(memory_semantics_instruction->type_id())
  137. ->opcode() != spv::Op::OpTypeInt) {
  138. return false;
  139. }
  140. // The size of the integer for memory scope and memory semantics
  141. // instructions must be equal to 32 bits.
  142. auto memory_scope_int_width =
  143. ir_context->get_def_use_mgr()
  144. ->GetDef(memory_scope_instruction->type_id())
  145. ->GetSingleWordInOperand(0);
  146. auto memory_semantics_int_width =
  147. ir_context->get_def_use_mgr()
  148. ->GetDef(memory_semantics_instruction->type_id())
  149. ->GetSingleWordInOperand(0);
  150. if (memory_scope_int_width != 32) {
  151. return false;
  152. }
  153. if (memory_semantics_int_width != 32) {
  154. return false;
  155. }
  156. // The memory scope constant value must be that of spv::Scope::Invocation.
  157. auto memory_scope_const_value =
  158. memory_scope_instruction->GetSingleWordInOperand(0);
  159. if (spv::Scope(memory_scope_const_value) != spv::Scope::Invocation) {
  160. return false;
  161. }
  162. // The memory semantics constant value must match the storage class of the
  163. // pointer being loaded from.
  164. auto memory_semantics_const_value = static_cast<spv::MemorySemanticsMask>(
  165. memory_semantics_instruction->GetSingleWordInOperand(0));
  166. if (memory_semantics_const_value !=
  167. fuzzerutil::GetMemorySemanticsForStorageClass(
  168. static_cast<spv::StorageClass>(
  169. pointer_type->GetSingleWordInOperand(0)))) {
  170. return false;
  171. }
  172. }
  173. // The value needs to be available at the insertion point.
  174. return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
  175. message_.value_id());
  176. }
  177. void TransformationStore::Apply(opt::IRContext* ir_context,
  178. TransformationContext* /*unused*/) const {
  179. if (message_.is_atomic()) {
  180. // OpAtomicStore instruction.
  181. auto insert_before =
  182. FindInstruction(message_.instruction_to_insert_before(), ir_context);
  183. auto new_instruction = MakeUnique<opt::Instruction>(
  184. ir_context, spv::Op::OpAtomicStore, 0, 0,
  185. opt::Instruction::OperandList(
  186. {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
  187. {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}},
  188. {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID,
  189. {message_.memory_semantics_id()}},
  190. {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
  191. auto new_instruction_ptr = new_instruction.get();
  192. insert_before->InsertBefore(std::move(new_instruction));
  193. // Inform the def-use manager about the new instruction and record its basic
  194. // block.
  195. ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
  196. ir_context->set_instr_block(new_instruction_ptr,
  197. ir_context->get_instr_block(insert_before));
  198. } else {
  199. // OpStore instruction.
  200. auto insert_before =
  201. FindInstruction(message_.instruction_to_insert_before(), ir_context);
  202. auto new_instruction = MakeUnique<opt::Instruction>(
  203. ir_context, spv::Op::OpStore, 0, 0,
  204. opt::Instruction::OperandList(
  205. {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
  206. {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
  207. auto new_instruction_ptr = new_instruction.get();
  208. insert_before->InsertBefore(std::move(new_instruction));
  209. // Inform the def-use manager about the new instruction and record its basic
  210. // block.
  211. ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
  212. ir_context->set_instr_block(new_instruction_ptr,
  213. ir_context->get_instr_block(insert_before));
  214. }
  215. }
  216. protobufs::Transformation TransformationStore::ToMessage() const {
  217. protobufs::Transformation result;
  218. *result.mutable_store() = message_;
  219. return result;
  220. }
  221. std::unordered_set<uint32_t> TransformationStore::GetFreshIds() const {
  222. return std::unordered_set<uint32_t>();
  223. }
  224. } // namespace fuzz
  225. } // namespace spvtools