• 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(SpvOpLoad,
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, SpvOpLoad, 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, SpvOpStore, 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, SpvOpStore, 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() || inst.opcode() == SpvOpUndef ||
149       inst.opcode() == SpvOpConstantNull) {
150     return false;
151   }
152 
153   opt::Instruction* type_inst =
154       ir_context->get_def_use_mgr()->GetDef(inst.type_id());
155   assert(type_inst != nullptr && "|inst| has invalid type id");
156 
157   // |inst| must be a pointer.
158   if (type_inst->opcode() != SpvOpTypePointer) {
159     return false;
160   }
161 
162   // |inst| must have a supported storage class.
163   switch (static_cast<SpvStorageClass>(type_inst->GetSingleWordInOperand(0))) {
164     case SpvStorageClassFunction:
165     case SpvStorageClassPrivate:
166     case SpvStorageClassWorkgroup:
167       break;
168     default:
169       return false;
170   }
171 
172   // |inst|'s pointee must consist of scalars and/or composites.
173   return fuzzerutil::CanCreateConstant(ir_context,
174                                        type_inst->GetSingleWordInOperand(1));
175 }
176 
GetFreshIds() const177 std::unordered_set<uint32_t> TransformationMutatePointer::GetFreshIds() const {
178   return {message_.fresh_id()};
179 }
180 
181 }  // namespace fuzz
182 }  // namespace spvtools
183