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 "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "source/fuzz/instruction_descriptor.h"
20 #include "test/fuzz/fuzz_test_util.h"
21
22 namespace spvtools {
23 namespace fuzz {
24 namespace {
25
TEST(TransformationMutatePointerTest,BasicTest)26 TEST(TransformationMutatePointerTest, BasicTest) {
27 std::string shader = R"(
28 OpCapability Shader
29 OpCapability VariablePointers
30 %1 = OpExtInstImport "GLSL.std.450"
31 OpMemoryModel Logical GLSL450
32 OpEntryPoint Fragment %4 "main"
33 OpExecutionMode %4 OriginUpperLeft
34 OpSource ESSL 310
35 %2 = OpTypeVoid
36 %3 = OpTypeFunction %2
37 %6 = OpTypeInt 32 1
38 %7 = OpTypeFloat 32
39 %34 = OpConstant %7 0
40 %36 = OpConstant %6 0
41 %14 = OpTypeVector %7 3
42 %35 = OpConstantComposite %14 %34 %34 %34
43 %15 = OpTypeMatrix %14 2
44 %8 = OpConstant %6 5
45 %9 = OpTypeArray %7 %8
46 %37 = OpConstantComposite %9 %34 %34 %34 %34 %34
47 %11 = OpTypeStruct
48 %38 = OpConstantComposite %11
49 %39 = OpConstantComposite %15 %35 %35
50 %31 = OpTypePointer Function %14
51 %10 = OpTypeStruct %7 %6 %9 %11 %15 %14
52 %40 = OpConstantComposite %10 %34 %36 %37 %38 %39 %35
53 %13 = OpTypePointer Function %10
54 %16 = OpTypePointer Private %10
55 %17 = OpTypePointer Workgroup %10
56 %18 = OpTypeStruct %16
57 %19 = OpTypePointer Private %18
58 %20 = OpVariable %16 Private
59 %21 = OpVariable %17 Workgroup
60 %22 = OpVariable %19 Private
61 %23 = OpTypePointer Output %6
62 %24 = OpVariable %23 Output
63 %27 = OpTypeFunction %2 %13
64 %33 = OpConstantNull %16
65 %4 = OpFunction %2 None %3
66 %5 = OpLabel
67 OpReturn
68 OpFunctionEnd
69 %28 = OpFunction %2 None %27
70 %29 = OpFunctionParameter %13
71 %30 = OpLabel
72 %25 = OpVariable %13 Function
73 %26 = OpAccessChain %31 %25 %8
74 OpReturn
75 OpFunctionEnd
76 )";
77
78 const auto env = SPV_ENV_UNIVERSAL_1_3;
79 const auto consumer = nullptr;
80 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
81 spvtools::ValidatorOptions validator_options;
82 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
83 kConsoleMessageConsumer));
84 TransformationContext transformation_context(
85 MakeUnique<FactManager>(context.get()), validator_options);
86 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(35);
87 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(39);
88
89 const auto insert_before = MakeInstructionDescriptor(26, SpvOpReturn, 0);
90
91 // 20 is not a fresh id.
92 ASSERT_FALSE(TransformationMutatePointer(20, 20, insert_before)
93 .IsApplicable(context.get(), transformation_context));
94
95 // |insert_before| instruction descriptor is invalid.
96 ASSERT_FALSE(TransformationMutatePointer(
97 20, 70, MakeInstructionDescriptor(26, SpvOpStore, 0))
98 .IsApplicable(context.get(), transformation_context));
99
100 // Can't insert OpLoad before OpVariable.
101 ASSERT_FALSE(TransformationMutatePointer(
102 20, 70, MakeInstructionDescriptor(26, SpvOpVariable, 0))
103 .IsApplicable(context.get(), transformation_context));
104
105 // |pointer_id| doesn't exist in the module.
106 ASSERT_FALSE(TransformationMutatePointer(70, 70, insert_before)
107 .IsApplicable(context.get(), transformation_context));
108
109 // |pointer_id| doesn't have a type id.
110 ASSERT_FALSE(TransformationMutatePointer(11, 70, insert_before)
111 .IsApplicable(context.get(), transformation_context));
112
113 // |pointer_id| is a result id of OpConstantNull.
114 ASSERT_FALSE(TransformationMutatePointer(33, 70, insert_before)
115 .IsApplicable(context.get(), transformation_context));
116
117 // |pointer_id| is not a pointer instruction.
118 ASSERT_FALSE(TransformationMutatePointer(8, 70, insert_before)
119 .IsApplicable(context.get(), transformation_context));
120
121 // |pointer_id| has invalid storage class
122 ASSERT_FALSE(TransformationMutatePointer(24, 70, insert_before)
123 .IsApplicable(context.get(), transformation_context));
124
125 // |pointer_id|'s pointee contains non-scalar and non-composite constituents.
126 ASSERT_FALSE(TransformationMutatePointer(22, 70, insert_before)
127 .IsApplicable(context.get(), transformation_context));
128
129 // There is no irrelevant zero constant to insert into the |pointer_id|.
130 ASSERT_FALSE(TransformationMutatePointer(20, 70, insert_before)
131 .IsApplicable(context.get(), transformation_context));
132
133 // |pointer_id| is not available before |insert_before|.
134 ASSERT_FALSE(TransformationMutatePointer(
135 26, 70, MakeInstructionDescriptor(26, SpvOpAccessChain, 0))
136 .IsApplicable(context.get(), transformation_context));
137
138 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(40);
139
140 uint32_t fresh_id = 70;
141 uint32_t pointer_ids[] = {
142 20, // Mutate Private variable.
143 21, // Mutate Workgroup variable.
144 25, // Mutate Function variable.
145 29, // Mutate function parameter.
146 26, // Mutate OpAccessChain.
147 };
148
149 for (auto pointer_id : pointer_ids) {
150 TransformationMutatePointer transformation(pointer_id, fresh_id++,
151 insert_before);
152 ASSERT_TRUE(
153 transformation.IsApplicable(context.get(), transformation_context));
154 ApplyAndCheckFreshIds(transformation, context.get(),
155 &transformation_context);
156 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
157 context.get(), validator_options, kConsoleMessageConsumer));
158 }
159
160 std::string after_transformation = R"(
161 OpCapability Shader
162 OpCapability VariablePointers
163 %1 = OpExtInstImport "GLSL.std.450"
164 OpMemoryModel Logical GLSL450
165 OpEntryPoint Fragment %4 "main"
166 OpExecutionMode %4 OriginUpperLeft
167 OpSource ESSL 310
168 %2 = OpTypeVoid
169 %3 = OpTypeFunction %2
170 %6 = OpTypeInt 32 1
171 %7 = OpTypeFloat 32
172 %34 = OpConstant %7 0
173 %36 = OpConstant %6 0
174 %14 = OpTypeVector %7 3
175 %35 = OpConstantComposite %14 %34 %34 %34
176 %15 = OpTypeMatrix %14 2
177 %8 = OpConstant %6 5
178 %9 = OpTypeArray %7 %8
179 %37 = OpConstantComposite %9 %34 %34 %34 %34 %34
180 %11 = OpTypeStruct
181 %38 = OpConstantComposite %11
182 %39 = OpConstantComposite %15 %35 %35
183 %31 = OpTypePointer Function %14
184 %10 = OpTypeStruct %7 %6 %9 %11 %15 %14
185 %40 = OpConstantComposite %10 %34 %36 %37 %38 %39 %35
186 %13 = OpTypePointer Function %10
187 %16 = OpTypePointer Private %10
188 %17 = OpTypePointer Workgroup %10
189 %18 = OpTypeStruct %16
190 %19 = OpTypePointer Private %18
191 %20 = OpVariable %16 Private
192 %21 = OpVariable %17 Workgroup
193 %22 = OpVariable %19 Private
194 %23 = OpTypePointer Output %6
195 %24 = OpVariable %23 Output
196 %27 = OpTypeFunction %2 %13
197 %33 = OpConstantNull %16
198 %4 = OpFunction %2 None %3
199 %5 = OpLabel
200 OpReturn
201 OpFunctionEnd
202 %28 = OpFunction %2 None %27
203 %29 = OpFunctionParameter %13
204 %30 = OpLabel
205 %25 = OpVariable %13 Function
206 %26 = OpAccessChain %31 %25 %8
207
208 ; modified Private variable
209 %70 = OpLoad %10 %20
210 OpStore %20 %40
211 OpStore %20 %70
212
213 ; modified Workgroup variable
214 %71 = OpLoad %10 %21
215 OpStore %21 %40
216 OpStore %21 %71
217
218 ; modified Function variable
219 %72 = OpLoad %10 %25
220 OpStore %25 %40
221 OpStore %25 %72
222
223 ; modified function parameter
224 %73 = OpLoad %10 %29
225 OpStore %29 %40
226 OpStore %29 %73
227
228 ; modified OpAccessChain
229 %74 = OpLoad %14 %26
230 OpStore %26 %35
231 OpStore %26 %74
232
233 OpReturn
234 OpFunctionEnd
235 )";
236
237 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
238 }
239
TEST(TransformationMutatePointerTest,HandlesUnreachableBlocks)240 TEST(TransformationMutatePointerTest, HandlesUnreachableBlocks) {
241 std::string shader = R"(
242 OpCapability Shader
243 %1 = OpExtInstImport "GLSL.std.450"
244 OpMemoryModel Logical GLSL450
245 OpEntryPoint Fragment %4 "main"
246 OpExecutionMode %4 OriginUpperLeft
247 OpSource ESSL 310
248 %2 = OpTypeVoid
249 %3 = OpTypeFunction %2
250 %6 = OpTypeInt 32 1
251 %7 = OpConstant %6 0
252 %8 = OpTypePointer Function %6
253 %11 = OpTypePointer Private %6
254 %12 = OpVariable %11 Private
255 %4 = OpFunction %2 None %3
256 %5 = OpLabel
257 %9 = OpVariable %8 Function
258 OpReturn
259 %10 = OpLabel
260 OpReturn
261 OpFunctionEnd
262 )";
263
264 const auto env = SPV_ENV_UNIVERSAL_1_3;
265 const auto consumer = nullptr;
266 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
267 spvtools::ValidatorOptions validator_options;
268 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
269 kConsoleMessageConsumer));
270 TransformationContext transformation_context(
271 MakeUnique<FactManager>(context.get()), validator_options);
272 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(7);
273
274 ASSERT_FALSE(
275 context->GetDominatorAnalysis(context->GetFunction(4))->IsReachable(10));
276
277 const auto insert_before = MakeInstructionDescriptor(10, SpvOpReturn, 0);
278
279 // Can mutate a global variable in an unreachable block.
280 TransformationMutatePointer transformation(12, 50, insert_before);
281 ASSERT_TRUE(
282 transformation.IsApplicable(context.get(), transformation_context));
283 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
284 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
285 kConsoleMessageConsumer));
286
287 std::string after_transformation = R"(
288 OpCapability Shader
289 %1 = OpExtInstImport "GLSL.std.450"
290 OpMemoryModel Logical GLSL450
291 OpEntryPoint Fragment %4 "main"
292 OpExecutionMode %4 OriginUpperLeft
293 OpSource ESSL 310
294 %2 = OpTypeVoid
295 %3 = OpTypeFunction %2
296 %6 = OpTypeInt 32 1
297 %7 = OpConstant %6 0
298 %8 = OpTypePointer Function %6
299 %11 = OpTypePointer Private %6
300 %12 = OpVariable %11 Private
301 %4 = OpFunction %2 None %3
302 %5 = OpLabel
303 %9 = OpVariable %8 Function
304 OpReturn
305 %10 = OpLabel
306 %50 = OpLoad %6 %12
307 OpStore %12 %7
308 OpStore %12 %50
309 OpReturn
310 OpFunctionEnd
311 )";
312
313 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
314 }
315
316 } // namespace
317 } // namespace fuzz
318 } // namespace spvtools
319