• 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_store.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19 
20 namespace spvtools {
21 namespace fuzz {
22 
TransformationStore(protobufs::TransformationStore message)23 TransformationStore::TransformationStore(protobufs::TransformationStore message)
24     : message_(std::move(message)) {}
25 
TransformationStore(uint32_t pointer_id,bool is_atomic,uint32_t memory_scope,uint32_t memory_semantics,uint32_t value_id,const protobufs::InstructionDescriptor & instruction_to_insert_before)26 TransformationStore::TransformationStore(
27     uint32_t pointer_id, bool is_atomic, uint32_t memory_scope,
28     uint32_t memory_semantics, uint32_t value_id,
29     const protobufs::InstructionDescriptor& instruction_to_insert_before) {
30   message_.set_pointer_id(pointer_id);
31   message_.set_is_atomic(is_atomic);
32   message_.set_memory_scope_id(memory_scope);
33   message_.set_memory_semantics_id(memory_semantics);
34   message_.set_value_id(value_id);
35   *message_.mutable_instruction_to_insert_before() =
36       instruction_to_insert_before;
37 }
38 
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const39 bool TransformationStore::IsApplicable(
40     opt::IRContext* ir_context,
41     const TransformationContext& transformation_context) const {
42   // The pointer must exist and have a type.
43   auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
44   if (!pointer || !pointer->type_id()) {
45     return false;
46   }
47 
48   // The pointer type must indeed be a pointer.
49   auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
50   assert(pointer_type && "Type id must be defined.");
51   if (pointer_type->opcode() != SpvOpTypePointer) {
52     return false;
53   }
54 
55   // The pointer must not be read only.
56   if (pointer->IsReadOnlyPointer()) {
57     return false;
58   }
59 
60   // We do not want to allow storing to null or undefined pointers.
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 store before it.
77   if (!message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
78                                    SpvOpStore, insert_before)) {
79     return false;
80   }
81   if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
82                                   SpvOpAtomicStore, insert_before)) {
83     return false;
84   }
85 
86   // The block we are inserting into needs to be dead, or else the pointee type
87   // of the pointer we are storing to needs to be irrelevant (otherwise the
88   // store could impact on the observable behaviour of the module).
89   if (!transformation_context.GetFactManager()->BlockIsDead(
90           ir_context->get_instr_block(insert_before)->id()) &&
91       !transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
92           message_.pointer_id())) {
93     return false;
94   }
95 
96   // The value being stored needs to exist and have a type.
97   auto value = ir_context->get_def_use_mgr()->GetDef(message_.value_id());
98   if (!value || !value->type_id()) {
99     return false;
100   }
101 
102   // The type of the value must match the pointee type.
103   if (pointer_type->GetSingleWordInOperand(1) != value->type_id()) {
104     return false;
105   }
106 
107   // The pointer needs to be available at the insertion point.
108   if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
109                                                   message_.pointer_id())) {
110     return false;
111   }
112 
113   if (message_.is_atomic()) {
114     // Check the exists of memory scope and memory semantics ids.
115     auto memory_scope_instruction =
116         ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id());
117     auto memory_semantics_instruction =
118         ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id());
119 
120     if (!memory_scope_instruction) {
121       return false;
122     }
123     if (!memory_semantics_instruction) {
124       return false;
125     }
126     // The memory scope and memory semantics instructions must have the
127     // 'OpConstant' opcode.
128     if (memory_scope_instruction->opcode() != SpvOpConstant) {
129       return false;
130     }
131     if (memory_semantics_instruction->opcode() != SpvOpConstant) {
132       return false;
133     }
134     // The memory scope and memory semantics need to be available before
135     // |insert_before|.
136     if (!fuzzerutil::IdIsAvailableBeforeInstruction(
137             ir_context, insert_before, message_.memory_scope_id())) {
138       return false;
139     }
140     if (!fuzzerutil::IdIsAvailableBeforeInstruction(
141             ir_context, insert_before, message_.memory_semantics_id())) {
142       return false;
143     }
144     // The memory scope and memory semantics instructions must have an Integer
145     // operand type with signedness does not matters.
146     if (ir_context->get_def_use_mgr()
147             ->GetDef(memory_scope_instruction->type_id())
148             ->opcode() != SpvOpTypeInt) {
149       return false;
150     }
151     if (ir_context->get_def_use_mgr()
152             ->GetDef(memory_semantics_instruction->type_id())
153             ->opcode() != SpvOpTypeInt) {
154       return false;
155     }
156 
157     // The size of the integer for memory scope and memory semantics
158     // instructions must be equal to 32 bits.
159     auto memory_scope_int_width =
160         ir_context->get_def_use_mgr()
161             ->GetDef(memory_scope_instruction->type_id())
162             ->GetSingleWordInOperand(0);
163     auto memory_semantics_int_width =
164         ir_context->get_def_use_mgr()
165             ->GetDef(memory_semantics_instruction->type_id())
166             ->GetSingleWordInOperand(0);
167 
168     if (memory_scope_int_width != 32) {
169       return false;
170     }
171     if (memory_semantics_int_width != 32) {
172       return false;
173     }
174 
175     // The memory scope constant value must be that of SpvScopeInvocation.
176     auto memory_scope_const_value =
177         memory_scope_instruction->GetSingleWordInOperand(0);
178     if (memory_scope_const_value != SpvScopeInvocation) {
179       return false;
180     }
181 
182     // The memory semantics constant value must match the storage class of the
183     // pointer being loaded from.
184     auto memory_semantics_const_value = static_cast<SpvMemorySemanticsMask>(
185         memory_semantics_instruction->GetSingleWordInOperand(0));
186     if (memory_semantics_const_value !=
187         fuzzerutil::GetMemorySemanticsForStorageClass(
188             static_cast<SpvStorageClass>(
189                 pointer_type->GetSingleWordInOperand(0)))) {
190       return false;
191     }
192   }
193 
194   // The value needs to be available at the insertion point.
195   return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
196                                                     message_.value_id());
197 }
198 
Apply(opt::IRContext * ir_context,TransformationContext *) const199 void TransformationStore::Apply(opt::IRContext* ir_context,
200                                 TransformationContext* /*unused*/) const {
201   if (message_.is_atomic()) {
202     // OpAtomicStore instruction.
203     auto insert_before =
204         FindInstruction(message_.instruction_to_insert_before(), ir_context);
205     auto new_instruction = MakeUnique<opt::Instruction>(
206         ir_context, SpvOpAtomicStore, 0, 0,
207         opt::Instruction::OperandList(
208             {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
209              {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}},
210              {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID,
211               {message_.memory_semantics_id()}},
212              {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
213     auto new_instruction_ptr = new_instruction.get();
214     insert_before->InsertBefore(std::move(new_instruction));
215     // Inform the def-use manager about the new instruction and record its basic
216     // block.
217     ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
218     ir_context->set_instr_block(new_instruction_ptr,
219                                 ir_context->get_instr_block(insert_before));
220 
221   } else {
222     // OpStore instruction.
223     auto insert_before =
224         FindInstruction(message_.instruction_to_insert_before(), ir_context);
225     auto new_instruction = MakeUnique<opt::Instruction>(
226         ir_context, SpvOpStore, 0, 0,
227         opt::Instruction::OperandList(
228             {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
229              {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
230     auto new_instruction_ptr = new_instruction.get();
231     insert_before->InsertBefore(std::move(new_instruction));
232     // Inform the def-use manager about the new instruction and record its basic
233     // block.
234     ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
235     ir_context->set_instr_block(new_instruction_ptr,
236                                 ir_context->get_instr_block(insert_before));
237   }
238 }
239 
ToMessage() const240 protobufs::Transformation TransformationStore::ToMessage() const {
241   protobufs::Transformation result;
242   *result.mutable_store() = message_;
243   return result;
244 }
245 
GetFreshIds() const246 std::unordered_set<uint32_t> TransformationStore::GetFreshIds() const {
247   return std::unordered_set<uint32_t>();
248 }
249 
250 }  // namespace fuzz
251 }  // namespace spvtools
252