transformation_function_call.cpp 6.8 KB

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