• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_load.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19 
20 namespace spvtools {
21 namespace fuzz {
22 
TransformationLoad(protobufs::TransformationLoad message)23 TransformationLoad::TransformationLoad(protobufs::TransformationLoad message)
24     : message_(std::move(message)) {}
25 
TransformationLoad(uint32_t fresh_id,uint32_t pointer_id,bool is_atomic,uint32_t memory_scope,uint32_t memory_semantics,const protobufs::InstructionDescriptor & instruction_to_insert_before)26 TransformationLoad::TransformationLoad(
27     uint32_t fresh_id, uint32_t pointer_id, bool is_atomic,
28     uint32_t memory_scope, uint32_t memory_semantics,
29     const protobufs::InstructionDescriptor& instruction_to_insert_before) {
30   message_.set_fresh_id(fresh_id);
31   message_.set_pointer_id(pointer_id);
32   message_.set_is_atomic(is_atomic);
33   message_.set_memory_scope_id(memory_scope);
34   message_.set_memory_semantics_id(memory_semantics);
35 
36   *message_.mutable_instruction_to_insert_before() =
37       instruction_to_insert_before;
38 }
39 
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const40 bool TransformationLoad::IsApplicable(
41     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
42   // The result id must be fresh.
43   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
44     return false;
45   }
46 
47   // The pointer must exist and have a type.
48   auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
49   if (!pointer || !pointer->type_id()) {
50     return false;
51   }
52   // The type must indeed be a pointer type.
53   auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
54   assert(pointer_type && "Type id must be defined.");
55   if (pointer_type->opcode() != SpvOpTypePointer) {
56     return false;
57   }
58   // We do not want to allow loading from null or undefined pointers, as it is
59   // not clear how punishing the consequences of doing so are from a semantics
60   // point of view.
61   switch (pointer->opcode()) {
62     case SpvOpConstantNull:
63     case SpvOpUndef:
64       return false;
65     default:
66       break;
67   }
68 
69   // Determine which instruction we should be inserting before.
70   auto insert_before =
71       FindInstruction(message_.instruction_to_insert_before(), ir_context);
72   // It must exist, ...
73   if (!insert_before) {
74     return false;
75   }
76   // ... and it must be legitimate to insert a load before it.
77   if (!message_.is_atomic() &&
78       !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) {
79     return false;
80   }
81 
82   if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
83                                   SpvOpAtomicLoad, insert_before)) {
84     return false;
85   }
86 
87   if (message_.is_atomic()) {
88     // Check the exists of memory scope and memory semantics ids.
89     auto memory_scope_instruction =
90         ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id());
91     auto memory_semantics_instruction =
92         ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id());
93 
94     if (!memory_scope_instruction) {
95       return false;
96     }
97     if (!memory_semantics_instruction) {
98       return false;
99     }
100     // The memory scope and memory semantics instructions must have the
101     // 'OpConstant' opcode.
102     if (memory_scope_instruction->opcode() != SpvOpConstant) {
103       return false;
104     }
105     if (memory_semantics_instruction->opcode() != SpvOpConstant) {
106       return false;
107     }
108     // The memory scope and memory semantics need to be available before
109     // |insert_before|.
110     if (!fuzzerutil::IdIsAvailableBeforeInstruction(
111             ir_context, insert_before, message_.memory_scope_id())) {
112       return false;
113     }
114     if (!fuzzerutil::IdIsAvailableBeforeInstruction(
115             ir_context, insert_before, message_.memory_semantics_id())) {
116       return false;
117     }
118     // The memory scope and memory semantics instructions must have an Integer
119     // operand type with signedness does not matters.
120     if (ir_context->get_def_use_mgr()
121             ->GetDef(memory_scope_instruction->type_id())
122             ->opcode() != SpvOpTypeInt) {
123       return false;
124     }
125     if (ir_context->get_def_use_mgr()
126             ->GetDef(memory_semantics_instruction->type_id())
127             ->opcode() != SpvOpTypeInt) {
128       return false;
129     }
130 
131     // The size of the integer for memory scope and memory semantics
132     // instructions must be equal to 32 bits.
133     auto memory_scope_int_width =
134         ir_context->get_def_use_mgr()
135             ->GetDef(memory_scope_instruction->type_id())
136             ->GetSingleWordInOperand(0);
137     auto memory_semantics_int_width =
138         ir_context->get_def_use_mgr()
139             ->GetDef(memory_semantics_instruction->type_id())
140             ->GetSingleWordInOperand(0);
141 
142     if (memory_scope_int_width != 32) {
143       return false;
144     }
145     if (memory_semantics_int_width != 32) {
146       return false;
147     }
148 
149     // The memory scope constant value must be that of SpvScopeInvocation.
150     auto memory_scope_const_value =
151         memory_scope_instruction->GetSingleWordInOperand(0);
152     if (memory_scope_const_value != SpvScopeInvocation) {
153       return false;
154     }
155 
156     // The memory semantics constant value must match the storage class of the
157     // pointer being loaded from.
158     auto memory_semantics_const_value = static_cast<SpvMemorySemanticsMask>(
159         memory_semantics_instruction->GetSingleWordInOperand(0));
160     if (memory_semantics_const_value !=
161         fuzzerutil::GetMemorySemanticsForStorageClass(
162             static_cast<SpvStorageClass>(
163                 pointer_type->GetSingleWordInOperand(0)))) {
164       return false;
165     }
166   }
167 
168   // The pointer needs to be available at the insertion point.
169   return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
170                                                     message_.pointer_id());
171 }
172 
Apply(opt::IRContext * ir_context,TransformationContext *) const173 void TransformationLoad::Apply(opt::IRContext* ir_context,
174                                TransformationContext* /*unused*/) const {
175   if (message_.is_atomic()) {
176     // OpAtomicLoad instruction.
177     uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
178         ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
179     fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
180     auto insert_before =
181         FindInstruction(message_.instruction_to_insert_before(), ir_context);
182     auto new_instruction = MakeUnique<opt::Instruction>(
183         ir_context, SpvOpAtomicLoad, result_type, message_.fresh_id(),
184         opt::Instruction::OperandList(
185             {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
186              {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}},
187              {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID,
188               {message_.memory_semantics_id()}}}));
189     auto new_instruction_ptr = new_instruction.get();
190     insert_before->InsertBefore(std::move(new_instruction));
191     // Inform the def-use manager about the new instruction and record its basic
192     // block.
193     ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
194     ir_context->set_instr_block(new_instruction_ptr,
195                                 ir_context->get_instr_block(insert_before));
196   } else {
197     // OpLoad instruction.
198     uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
199         ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
200     fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
201     auto insert_before =
202         FindInstruction(message_.instruction_to_insert_before(), ir_context);
203     auto new_instruction = MakeUnique<opt::Instruction>(
204         ir_context, SpvOpLoad, result_type, message_.fresh_id(),
205         opt::Instruction::OperandList(
206             {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}));
207     auto new_instruction_ptr = new_instruction.get();
208     insert_before->InsertBefore(std::move(new_instruction));
209     // Inform the def-use manager about the new instruction and record its basic
210     // block.
211     ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
212     ir_context->set_instr_block(new_instruction_ptr,
213                                 ir_context->get_instr_block(insert_before));
214   }
215 }
216 
ToMessage() const217 protobufs::Transformation TransformationLoad::ToMessage() const {
218   protobufs::Transformation result;
219   *result.mutable_load() = message_;
220   return result;
221 }
222 
GetFreshIds() const223 std::unordered_set<uint32_t> TransformationLoad::GetFreshIds() const {
224   return {message_.fresh_id()};
225 }
226 
227 }  // namespace fuzz
228 }  // namespace spvtools
229