transformation_function_call.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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_function_call.h"
  15. #include "source/fuzz/call_graph.h"
  16. #include "source/fuzz/fuzzer_util.h"
  17. #include "source/fuzz/instruction_descriptor.h"
  18. namespace spvtools {
  19. namespace fuzz {
  20. TransformationFunctionCall::TransformationFunctionCall(
  21. protobufs::TransformationFunctionCall message)
  22. : message_(std::move(message)) {}
  23. TransformationFunctionCall::TransformationFunctionCall(
  24. uint32_t fresh_id, uint32_t callee_id,
  25. const std::vector<uint32_t>& argument_id,
  26. const protobufs::InstructionDescriptor& instruction_to_insert_before) {
  27. message_.set_fresh_id(fresh_id);
  28. message_.set_callee_id(callee_id);
  29. for (auto argument : argument_id) {
  30. message_.add_argument_id(argument);
  31. }
  32. *message_.mutable_instruction_to_insert_before() =
  33. instruction_to_insert_before;
  34. }
  35. bool TransformationFunctionCall::IsApplicable(
  36. opt::IRContext* ir_context,
  37. const TransformationContext& transformation_context) const {
  38. // The result id must be fresh
  39. if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
  40. return false;
  41. }
  42. // The function must exist
  43. auto callee_inst =
  44. ir_context->get_def_use_mgr()->GetDef(message_.callee_id());
  45. if (!callee_inst || callee_inst->opcode() != spv::Op::OpFunction) {
  46. return false;
  47. }
  48. // The function must not be an entry point
  49. if (fuzzerutil::FunctionIsEntryPoint(ir_context, message_.callee_id())) {
  50. return false;
  51. }
  52. auto callee_type_inst = ir_context->get_def_use_mgr()->GetDef(
  53. callee_inst->GetSingleWordInOperand(1));
  54. assert(callee_type_inst->opcode() == spv::Op::OpTypeFunction &&
  55. "Bad function type.");
  56. // The number of expected function arguments must match the number of given
  57. // arguments. The number of expected arguments is one less than the function
  58. // type's number of input operands, as one operand is for the return type.
  59. if (callee_type_inst->NumInOperands() - 1 !=
  60. static_cast<uint32_t>(message_.argument_id().size())) {
  61. return false;
  62. }
  63. // The instruction descriptor must refer to a position where it is valid to
  64. // insert the call
  65. auto insert_before =
  66. FindInstruction(message_.instruction_to_insert_before(), ir_context);
  67. if (!insert_before) {
  68. return false;
  69. }
  70. if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpFunctionCall,
  71. insert_before)) {
  72. return false;
  73. }
  74. auto block = ir_context->get_instr_block(insert_before);
  75. auto enclosing_function = block->GetParent();
  76. // If the block is not dead, the function must be livesafe
  77. bool block_is_dead =
  78. transformation_context.GetFactManager()->BlockIsDead(block->id());
  79. if (!block_is_dead &&
  80. !transformation_context.GetFactManager()->FunctionIsLivesafe(
  81. message_.callee_id())) {
  82. return false;
  83. }
  84. // The ids must all match and have the right types and satisfy rules on
  85. // pointers. If the block is not dead, pointers must be arbitrary.
  86. for (uint32_t arg_index = 0;
  87. arg_index < static_cast<uint32_t>(message_.argument_id().size());
  88. arg_index++) {
  89. opt::Instruction* arg_inst =
  90. ir_context->get_def_use_mgr()->GetDef(message_.argument_id(arg_index));
  91. if (!arg_inst) {
  92. // The given argument does not correspond to an instruction.
  93. return false;
  94. }
  95. if (!arg_inst->type_id()) {
  96. // The given argument does not have a type; it is thus not suitable.
  97. }
  98. if (arg_inst->type_id() !=
  99. callee_type_inst->GetSingleWordInOperand(arg_index + 1)) {
  100. // Argument type mismatch.
  101. return false;
  102. }
  103. opt::Instruction* arg_type_inst =
  104. ir_context->get_def_use_mgr()->GetDef(arg_inst->type_id());
  105. if (arg_type_inst->opcode() == spv::Op::OpTypePointer) {
  106. switch (arg_inst->opcode()) {
  107. case spv::Op::OpFunctionParameter:
  108. case spv::Op::OpVariable:
  109. // These are OK
  110. break;
  111. default:
  112. // Other pointer ids cannot be passed as parameters
  113. return false;
  114. }
  115. if (!block_is_dead &&
  116. !transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
  117. arg_inst->result_id())) {
  118. // This is not a dead block, so pointer parameters passed to the called
  119. // function might really have their contents modified. We thus require
  120. // such pointers to be to arbitrary-valued variables, which this is not.
  121. return false;
  122. }
  123. }
  124. // The argument id needs to be available (according to dominance rules) at
  125. // the point where the call will occur.
  126. if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
  127. arg_inst->result_id())) {
  128. return false;
  129. }
  130. }
  131. // Introducing the call must not lead to recursion.
  132. if (message_.callee_id() == enclosing_function->result_id()) {
  133. // This would be direct recursion.
  134. return false;
  135. }
  136. // Ensure the call would not lead to indirect recursion.
  137. return !CallGraph(ir_context)
  138. .GetIndirectCallees(message_.callee_id())
  139. .count(block->GetParent()->result_id());
  140. }
  141. void TransformationFunctionCall::Apply(
  142. opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
  143. // Update the module's bound to reflect the fresh id for the result of the
  144. // function call.
  145. fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
  146. // Get the return type of the function being called.
  147. uint32_t return_type =
  148. ir_context->get_def_use_mgr()->GetDef(message_.callee_id())->type_id();
  149. // Populate the operands to the call instruction, with the function id and the
  150. // arguments.
  151. opt::Instruction::OperandList operands;
  152. operands.push_back({SPV_OPERAND_TYPE_ID, {message_.callee_id()}});
  153. for (auto arg : message_.argument_id()) {
  154. operands.push_back({SPV_OPERAND_TYPE_ID, {arg}});
  155. }
  156. // Insert the function call before the instruction specified in the message.
  157. FindInstruction(message_.instruction_to_insert_before(), ir_context)
  158. ->InsertBefore(MakeUnique<opt::Instruction>(
  159. ir_context, spv::Op::OpFunctionCall, return_type, message_.fresh_id(),
  160. operands));
  161. // Invalidate all analyses since we have changed the module.
  162. ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
  163. }
  164. protobufs::Transformation TransformationFunctionCall::ToMessage() const {
  165. protobufs::Transformation result;
  166. *result.mutable_function_call() = message_;
  167. return result;
  168. }
  169. std::unordered_set<uint32_t> TransformationFunctionCall::GetFreshIds() const {
  170. return {message_.fresh_id()};
  171. }
  172. } // namespace fuzz
  173. } // namespace spvtools