transformation_load.cpp 8.7 KB

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