• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2020 Vasyl Teliman
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_mutate_pointer.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19 
20 namespace spvtools {
21 namespace fuzz {
22 
TransformationMutatePointer(protobufs::TransformationMutatePointer message)23 TransformationMutatePointer::TransformationMutatePointer(
24     protobufs::TransformationMutatePointer message)
25     : message_(std::move(message)) {}
26 
TransformationMutatePointer(uint32_t pointer_id,uint32_t fresh_id,const protobufs::InstructionDescriptor & insert_before)27 TransformationMutatePointer::TransformationMutatePointer(
28     uint32_t pointer_id, uint32_t fresh_id,
29     const protobufs::InstructionDescriptor& insert_before) {
30   message_.set_pointer_id(pointer_id);
31   message_.set_fresh_id(fresh_id);
32   *message_.mutable_insert_before() = insert_before;
33 }
34 
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const35 bool TransformationMutatePointer::IsApplicable(
36     opt::IRContext* ir_context,
37     const TransformationContext& transformation_context) const {
38   // Check that |fresh_id| is fresh.
39   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
40     return false;
41   }
42 
43   auto* insert_before_inst =
44       FindInstruction(message_.insert_before(), ir_context);
45 
46   // Check that |insert_before| is a valid instruction descriptor.
47   if (!insert_before_inst) {
48     return false;
49   }
50 
51   // Check that it is possible to insert OpLoad and OpStore before
52   // |insert_before_inst|. We are only using OpLoad here since the result does
53   // not depend on the opcode.
54   if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad,
55                                                     insert_before_inst)) {
56     return false;
57   }
58 
59   const auto* pointer_inst =
60       ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
61 
62   // Check that |pointer_id| is a result id of a valid pointer instruction.
63   if (!pointer_inst || !IsValidPointerInstruction(ir_context, *pointer_inst)) {
64     return false;
65   }
66 
67   // Check that the module contains an irrelevant constant that will be used to
68   // mutate |pointer_inst|. The constant is irrelevant so that the latter
69   // transformation can change its value to something more interesting.
70   auto constant_id = fuzzerutil::MaybeGetZeroConstant(
71       ir_context, transformation_context,
72       fuzzerutil::GetPointeeTypeIdFromPointerType(ir_context,
73                                                   pointer_inst->type_id()),
74       true);
75   if (!constant_id) {
76     return false;
77   }
78 
79   assert(fuzzerutil::IdIsAvailableBeforeInstruction(
80              ir_context, insert_before_inst, constant_id) &&
81          "Global constant instruction is not available before "
82          "|insert_before_inst|");
83 
84   // Check that |pointer_inst| is available before |insert_before_inst|.
85   return fuzzerutil::IdIsAvailableBeforeInstruction(
86       ir_context, insert_before_inst, pointer_inst->result_id());
87 }
88 
Apply(opt::IRContext * ir_context,TransformationContext * transformation_context) const89 void TransformationMutatePointer::Apply(
90     opt::IRContext* ir_context,
91     TransformationContext* transformation_context) const {
92   auto* insert_before_inst =
93       FindInstruction(message_.insert_before(), ir_context);
94   assert(insert_before_inst && "|insert_before| descriptor is invalid");
95   opt::BasicBlock* enclosing_block =
96       ir_context->get_instr_block(insert_before_inst);
97 
98   auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
99       ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
100 
101   // Back up the original value.
102   auto backup_instruction = MakeUnique<opt::Instruction>(
103       ir_context, spv::Op::OpLoad, pointee_type_id, message_.fresh_id(),
104       opt::Instruction::OperandList{
105           {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}});
106   auto backup_instruction_ptr = backup_instruction.get();
107   insert_before_inst->InsertBefore(std::move(backup_instruction));
108   ir_context->get_def_use_mgr()->AnalyzeInstDefUse(backup_instruction_ptr);
109   ir_context->set_instr_block(backup_instruction_ptr, enclosing_block);
110 
111   // Insert a new value.
112   auto new_value_instruction = MakeUnique<opt::Instruction>(
113       ir_context, spv::Op::OpStore, 0, 0,
114       opt::Instruction::OperandList{
115           {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
116           {SPV_OPERAND_TYPE_ID,
117            {fuzzerutil::MaybeGetZeroConstant(
118                ir_context, *transformation_context, pointee_type_id, true)}}});
119   auto new_value_instruction_ptr = new_value_instruction.get();
120   insert_before_inst->InsertBefore(std::move(new_value_instruction));
121   ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_value_instruction_ptr);
122   ir_context->set_instr_block(new_value_instruction_ptr, enclosing_block);
123 
124   // Restore the original value.
125   auto restore_instruction = MakeUnique<opt::Instruction>(
126       ir_context, spv::Op::OpStore, 0, 0,
127       opt::Instruction::OperandList{
128           {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
129           {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}});
130   auto restore_instruction_ptr = restore_instruction.get();
131   insert_before_inst->InsertBefore(std::move(restore_instruction));
132   ir_context->get_def_use_mgr()->AnalyzeInstDefUse(restore_instruction_ptr);
133   ir_context->set_instr_block(restore_instruction_ptr, enclosing_block);
134 
135   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
136 }
137 
ToMessage() const138 protobufs::Transformation TransformationMutatePointer::ToMessage() const {
139   protobufs::Transformation result;
140   *result.mutable_mutate_pointer() = message_;
141   return result;
142 }
143 
IsValidPointerInstruction(opt::IRContext * ir_context,const opt::Instruction & inst)144 bool TransformationMutatePointer::IsValidPointerInstruction(
145     opt::IRContext* ir_context, const opt::Instruction& inst) {
146   // |inst| must have both result id and type id and it may not cause undefined
147   // behaviour.
148   if (!inst.result_id() || !inst.type_id() ||
149       inst.opcode() == spv::Op::OpUndef ||
150       inst.opcode() == spv::Op::OpConstantNull) {
151     return false;
152   }
153 
154   opt::Instruction* type_inst =
155       ir_context->get_def_use_mgr()->GetDef(inst.type_id());
156   assert(type_inst != nullptr && "|inst| has invalid type id");
157 
158   // |inst| must be a pointer.
159   if (type_inst->opcode() != spv::Op::OpTypePointer) {
160     return false;
161   }
162 
163   // |inst| must have a supported storage class.
164   switch (
165       static_cast<spv::StorageClass>(type_inst->GetSingleWordInOperand(0))) {
166     case spv::StorageClass::Function:
167     case spv::StorageClass::Private:
168     case spv::StorageClass::Workgroup:
169       break;
170     default:
171       return false;
172   }
173 
174   // |inst|'s pointee must consist of scalars and/or composites.
175   return fuzzerutil::CanCreateConstant(ir_context,
176                                        type_inst->GetSingleWordInOperand(1));
177 }
178 
GetFreshIds() const179 std::unordered_set<uint32_t> TransformationMutatePointer::GetFreshIds() const {
180   return {message_.fresh_id()};
181 }
182 
183 }  // namespace fuzz
184 }  // namespace spvtools
185