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