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