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(const spvtools::fuzz::protobufs::TransformationStore & message)23 TransformationStore::TransformationStore(
24 const spvtools::fuzz::protobufs::TransformationStore& message)
25 : message_(message) {}
26
TransformationStore(uint32_t pointer_id,uint32_t value_id,const protobufs::InstructionDescriptor & instruction_to_insert_before)27 TransformationStore::TransformationStore(
28 uint32_t pointer_id, uint32_t value_id,
29 const protobufs::InstructionDescriptor& instruction_to_insert_before) {
30 message_.set_pointer_id(pointer_id);
31 message_.set_value_id(value_id);
32 *message_.mutable_instruction_to_insert_before() =
33 instruction_to_insert_before;
34 }
35
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const36 bool TransformationStore::IsApplicable(
37 opt::IRContext* ir_context,
38 const TransformationContext& transformation_context) const {
39 // The pointer must exist and have a type.
40 auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
41 if (!pointer || !pointer->type_id()) {
42 return false;
43 }
44
45 // The pointer type must indeed be a pointer.
46 auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
47 assert(pointer_type && "Type id must be defined.");
48 if (pointer_type->opcode() != SpvOpTypePointer) {
49 return false;
50 }
51
52 // The pointer must not be read only.
53 if (pointer->IsReadOnlyPointer()) {
54 return false;
55 }
56
57 // We do not want to allow storing to null or undefined pointers.
58 switch (pointer->opcode()) {
59 case SpvOpConstantNull:
60 case SpvOpUndef:
61 return false;
62 default:
63 break;
64 }
65
66 // Determine which instruction we should be inserting before.
67 auto insert_before =
68 FindInstruction(message_.instruction_to_insert_before(), ir_context);
69 // It must exist, ...
70 if (!insert_before) {
71 return false;
72 }
73 // ... and it must be legitimate to insert a store before it.
74 if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
75 insert_before)) {
76 return false;
77 }
78
79 // The block we are inserting into needs to be dead, or else the pointee type
80 // of the pointer we are storing to needs to be irrelevant (otherwise the
81 // store could impact on the observable behaviour of the module).
82 if (!transformation_context.GetFactManager()->BlockIsDead(
83 ir_context->get_instr_block(insert_before)->id()) &&
84 !transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
85 message_.pointer_id())) {
86 return false;
87 }
88
89 // The value being stored needs to exist and have a type.
90 auto value = ir_context->get_def_use_mgr()->GetDef(message_.value_id());
91 if (!value || !value->type_id()) {
92 return false;
93 }
94
95 // The type of the value must match the pointee type.
96 if (pointer_type->GetSingleWordInOperand(1) != value->type_id()) {
97 return false;
98 }
99
100 // The pointer needs to be available at the insertion point.
101 if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
102 message_.pointer_id())) {
103 return false;
104 }
105
106 // The value needs to be available at the insertion point.
107 return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
108 message_.value_id());
109 }
110
Apply(opt::IRContext * ir_context,TransformationContext *) const111 void TransformationStore::Apply(opt::IRContext* ir_context,
112 TransformationContext* /*unused*/) const {
113 FindInstruction(message_.instruction_to_insert_before(), ir_context)
114 ->InsertBefore(MakeUnique<opt::Instruction>(
115 ir_context, SpvOpStore, 0, 0,
116 opt::Instruction::OperandList(
117 {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
118 {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
119 ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
120 }
121
ToMessage() const122 protobufs::Transformation TransformationStore::ToMessage() const {
123 protobufs::Transformation result;
124 *result.mutable_store() = message_;
125 return result;
126 }
127
GetFreshIds() const128 std::unordered_set<uint32_t> TransformationStore::GetFreshIds() const {
129 return std::unordered_set<uint32_t>();
130 }
131
132 } // namespace fuzz
133 } // namespace spvtools
134