1 // Copyright (c) 2019 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 "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(TransformationStoreTest,BasicTest)26 TEST(TransformationStoreTest, BasicTest) {
27 std::string shader = R"(
28 OpCapability Shader
29 %1 = OpExtInstImport "GLSL.std.450"
30 OpMemoryModel Logical GLSL450
31 OpEntryPoint Fragment %4 "main" %92 %52 %53
32 OpExecutionMode %4 OriginUpperLeft
33 OpSource ESSL 310
34 OpDecorate %92 BuiltIn FragCoord
35 %2 = OpTypeVoid
36 %3 = OpTypeFunction %2
37 %6 = OpTypeInt 32 1
38 %7 = OpTypeFloat 32
39 %8 = OpTypeStruct %6 %7
40 %9 = OpTypePointer Function %8
41 %10 = OpTypeFunction %6 %9
42 %14 = OpConstant %6 0
43 %15 = OpTypePointer Function %6
44 %51 = OpTypePointer Private %6
45 %21 = OpConstant %6 2
46 %23 = OpConstant %6 1
47 %24 = OpConstant %7 1
48 %25 = OpTypePointer Function %7
49 %50 = OpTypePointer Private %7
50 %34 = OpTypeBool
51 %35 = OpConstantFalse %34
52 %60 = OpConstantNull %50
53 %61 = OpUndef %51
54 %52 = OpVariable %50 Private
55 %53 = OpVariable %51 Private
56 %80 = OpConstantComposite %8 %21 %24
57 %90 = OpTypeVector %7 4
58 %91 = OpTypePointer Input %90
59 %92 = OpVariable %91 Input
60 %93 = OpConstantComposite %90 %24 %24 %24 %24
61 %4 = OpFunction %2 None %3
62 %5 = OpLabel
63 %20 = OpVariable %9 Function
64 %27 = OpVariable %9 Function ; irrelevant
65 %22 = OpAccessChain %15 %20 %14
66 %44 = OpCopyObject %9 %20
67 %26 = OpAccessChain %25 %20 %23
68 %29 = OpFunctionCall %6 %12 %27
69 %30 = OpAccessChain %15 %20 %14
70 %45 = OpCopyObject %15 %30
71 %81 = OpCopyObject %9 %27 ; irrelevant
72 %33 = OpAccessChain %15 %20 %14
73 OpSelectionMerge %37 None
74 OpBranchConditional %35 %36 %37
75 %36 = OpLabel
76 %38 = OpAccessChain %15 %20 %14
77 %40 = OpAccessChain %15 %20 %14
78 %43 = OpAccessChain %15 %20 %14
79 %82 = OpCopyObject %9 %27 ; irrelevant
80 OpBranch %37
81 %37 = OpLabel
82 OpReturn
83 OpFunctionEnd
84 %12 = OpFunction %6 None %10
85 %11 = OpFunctionParameter %9 ; irrelevant
86 %13 = OpLabel
87 %46 = OpCopyObject %9 %11 ; irrelevant
88 %16 = OpAccessChain %15 %11 %14 ; irrelevant
89 %95 = OpCopyObject %8 %80
90 OpReturnValue %21
91 OpFunctionEnd
92 )";
93
94 const auto env = SPV_ENV_UNIVERSAL_1_4;
95 const auto consumer = nullptr;
96 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
97 spvtools::ValidatorOptions validator_options;
98 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
99 kConsoleMessageConsumer));
100 TransformationContext transformation_context(
101 MakeUnique<FactManager>(context.get()), validator_options);
102 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
103 27);
104 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
105 11);
106 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
107 46);
108 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
109 16);
110 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
111 52);
112 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
113 81);
114 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
115 82);
116
117 transformation_context.GetFactManager()->AddFactBlockIsDead(36);
118
119 // Variables with pointee types:
120 // 52 - ptr_to(7)
121 // 53 - ptr_to(6)
122 // 20 - ptr_to(8)
123 // 27 - ptr_to(8) - irrelevant
124 // 92 - ptr_to(90) - read only
125
126 // Access chains with pointee type:
127 // 22 - ptr_to(6)
128 // 26 - ptr_to(6)
129 // 30 - ptr_to(6)
130 // 33 - ptr_to(6)
131 // 38 - ptr_to(6)
132 // 40 - ptr_to(6)
133 // 43 - ptr_to(6)
134 // 16 - ptr_to(6) - irrelevant
135
136 // Copied object with pointee type:
137 // 44 - ptr_to(8)
138 // 45 - ptr_to(6)
139 // 46 - ptr_to(8) - irrelevant
140 // 81 - ptr_to(8) - irrelevant
141 // 82 - ptr_to(8) - irrelevant
142
143 // Function parameters with pointee type:
144 // 11 - ptr_to(8) - irrelevant
145
146 // Pointers that cannot be used:
147 // 60 - null
148 // 61 - undefined
149
150 // Bad: attempt to store to 11 from outside its function
151 ASSERT_FALSE(TransformationStore(
152 11, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
153 .IsApplicable(context.get(), transformation_context));
154
155 // Bad: pointer is not available
156 ASSERT_FALSE(TransformationStore(
157 81, 80, MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
158 .IsApplicable(context.get(), transformation_context));
159
160 // Bad: attempt to insert before OpVariable
161 ASSERT_FALSE(TransformationStore(
162 52, 24, MakeInstructionDescriptor(27, SpvOpVariable, 0))
163 .IsApplicable(context.get(), transformation_context));
164
165 // Bad: pointer id does not exist
166 ASSERT_FALSE(TransformationStore(
167 1000, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
168 .IsApplicable(context.get(), transformation_context));
169
170 // Bad: pointer id exists but does not have a type
171 ASSERT_FALSE(TransformationStore(
172 5, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
173 .IsApplicable(context.get(), transformation_context));
174
175 // Bad: pointer id exists and has a type, but is not a pointer
176 ASSERT_FALSE(TransformationStore(
177 24, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
178 .IsApplicable(context.get(), transformation_context));
179
180 // Bad: attempt to store to a null pointer
181 ASSERT_FALSE(TransformationStore(
182 60, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
183 .IsApplicable(context.get(), transformation_context));
184
185 // Bad: attempt to store to an undefined pointer
186 ASSERT_FALSE(TransformationStore(
187 61, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
188 .IsApplicable(context.get(), transformation_context));
189
190 // Bad: %82 is not available at the program point
191 ASSERT_FALSE(
192 TransformationStore(82, 80, MakeInstructionDescriptor(37, SpvOpReturn, 0))
193 .IsApplicable(context.get(), transformation_context));
194
195 // Bad: value id does not exist
196 ASSERT_FALSE(TransformationStore(
197 27, 1000, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
198 .IsApplicable(context.get(), transformation_context));
199
200 // Bad: value id exists but does not have a type
201 ASSERT_FALSE(TransformationStore(
202 27, 15, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
203 .IsApplicable(context.get(), transformation_context));
204
205 // Bad: value id exists but has the wrong type
206 ASSERT_FALSE(TransformationStore(
207 27, 14, MakeInstructionDescriptor(38, SpvOpAccessChain, 0))
208 .IsApplicable(context.get(), transformation_context));
209
210 // Bad: attempt to store to read-only variable
211 ASSERT_FALSE(TransformationStore(
212 92, 93, MakeInstructionDescriptor(40, SpvOpAccessChain, 0))
213 .IsApplicable(context.get(), transformation_context));
214
215 // Bad: value is not available
216 ASSERT_FALSE(TransformationStore(
217 27, 95, MakeInstructionDescriptor(40, SpvOpAccessChain, 0))
218 .IsApplicable(context.get(), transformation_context));
219
220 // Bad: variable being stored to does not have an irrelevant pointee value,
221 // and the store is not in a dead block.
222 ASSERT_FALSE(TransformationStore(
223 20, 95, MakeInstructionDescriptor(45, SpvOpCopyObject, 0))
224 .IsApplicable(context.get(), transformation_context));
225
226 // The described instruction does not exist.
227 ASSERT_FALSE(TransformationStore(
228 27, 80, MakeInstructionDescriptor(1000, SpvOpAccessChain, 0))
229 .IsApplicable(context.get(), transformation_context));
230
231 {
232 // Store to irrelevant variable from dead block.
233 TransformationStore transformation(
234 27, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
235 ASSERT_TRUE(
236 transformation.IsApplicable(context.get(), transformation_context));
237 ApplyAndCheckFreshIds(transformation, context.get(),
238 &transformation_context);
239 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
240 context.get(), validator_options, kConsoleMessageConsumer));
241 }
242
243 {
244 // Store to irrelevant variable from live block.
245 TransformationStore transformation(
246 11, 95, MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
247 ASSERT_TRUE(
248 transformation.IsApplicable(context.get(), transformation_context));
249 ApplyAndCheckFreshIds(transformation, context.get(),
250 &transformation_context);
251 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
252 context.get(), validator_options, kConsoleMessageConsumer));
253 }
254
255 {
256 // Store to irrelevant variable from live block.
257 TransformationStore transformation(
258 46, 80, MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
259 ASSERT_TRUE(
260 transformation.IsApplicable(context.get(), transformation_context));
261 ApplyAndCheckFreshIds(transformation, context.get(),
262 &transformation_context);
263 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
264 context.get(), validator_options, kConsoleMessageConsumer));
265 }
266
267 {
268 // Store to irrelevant variable from live block.
269 TransformationStore transformation(
270 16, 21, MakeInstructionDescriptor(95, SpvOpReturnValue, 0));
271 ASSERT_TRUE(
272 transformation.IsApplicable(context.get(), transformation_context));
273 ApplyAndCheckFreshIds(transformation, context.get(),
274 &transformation_context);
275 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
276 context.get(), validator_options, kConsoleMessageConsumer));
277 }
278
279 {
280 // Store to non-irrelevant variable from dead block.
281 TransformationStore transformation(
282 53, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0));
283 ASSERT_TRUE(
284 transformation.IsApplicable(context.get(), transformation_context));
285 ApplyAndCheckFreshIds(transformation, context.get(),
286 &transformation_context);
287 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
288 context.get(), validator_options, kConsoleMessageConsumer));
289 }
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" %92 %52 %53
296 OpExecutionMode %4 OriginUpperLeft
297 OpSource ESSL 310
298 OpDecorate %92 BuiltIn FragCoord
299 %2 = OpTypeVoid
300 %3 = OpTypeFunction %2
301 %6 = OpTypeInt 32 1
302 %7 = OpTypeFloat 32
303 %8 = OpTypeStruct %6 %7
304 %9 = OpTypePointer Function %8
305 %10 = OpTypeFunction %6 %9
306 %14 = OpConstant %6 0
307 %15 = OpTypePointer Function %6
308 %51 = OpTypePointer Private %6
309 %21 = OpConstant %6 2
310 %23 = OpConstant %6 1
311 %24 = OpConstant %7 1
312 %25 = OpTypePointer Function %7
313 %50 = OpTypePointer Private %7
314 %34 = OpTypeBool
315 %35 = OpConstantFalse %34
316 %60 = OpConstantNull %50
317 %61 = OpUndef %51
318 %52 = OpVariable %50 Private
319 %53 = OpVariable %51 Private
320 %80 = OpConstantComposite %8 %21 %24
321 %90 = OpTypeVector %7 4
322 %91 = OpTypePointer Input %90
323 %92 = OpVariable %91 Input
324 %93 = OpConstantComposite %90 %24 %24 %24 %24
325 %4 = OpFunction %2 None %3
326 %5 = OpLabel
327 %20 = OpVariable %9 Function
328 %27 = OpVariable %9 Function ; irrelevant
329 %22 = OpAccessChain %15 %20 %14
330 %44 = OpCopyObject %9 %20
331 %26 = OpAccessChain %25 %20 %23
332 %29 = OpFunctionCall %6 %12 %27
333 %30 = OpAccessChain %15 %20 %14
334 %45 = OpCopyObject %15 %30
335 %81 = OpCopyObject %9 %27 ; irrelevant
336 %33 = OpAccessChain %15 %20 %14
337 OpSelectionMerge %37 None
338 OpBranchConditional %35 %36 %37
339 %36 = OpLabel
340 OpStore %27 %80
341 OpStore %53 %21
342 %38 = OpAccessChain %15 %20 %14
343 %40 = OpAccessChain %15 %20 %14
344 %43 = OpAccessChain %15 %20 %14
345 %82 = OpCopyObject %9 %27 ; irrelevant
346 OpBranch %37
347 %37 = OpLabel
348 OpReturn
349 OpFunctionEnd
350 %12 = OpFunction %6 None %10
351 %11 = OpFunctionParameter %9 ; irrelevant
352 %13 = OpLabel
353 %46 = OpCopyObject %9 %11 ; irrelevant
354 %16 = OpAccessChain %15 %11 %14 ; irrelevant
355 %95 = OpCopyObject %8 %80
356 OpStore %11 %95
357 OpStore %46 %80
358 OpStore %16 %21
359 OpReturnValue %21
360 OpFunctionEnd
361 )";
362 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
363 }
364
TEST(TransformationStoreTest,DoNotAllowStoresToReadOnlyMemory)365 TEST(TransformationStoreTest, DoNotAllowStoresToReadOnlyMemory) {
366 std::string shader = R"(
367 OpCapability Shader
368 %1 = OpExtInstImport "GLSL.std.450"
369 OpMemoryModel Logical GLSL450
370 OpEntryPoint Fragment %4 "main"
371 OpExecutionMode %4 OriginUpperLeft
372 OpSource ESSL 320
373 OpMemberDecorate %10 0 Offset 0
374 OpMemberDecorate %10 1 Offset 4
375 OpDecorate %10 Block
376 OpMemberDecorate %23 0 Offset 0
377 OpDecorate %23 Block
378 OpDecorate %25 DescriptorSet 0
379 OpDecorate %25 Binding 0
380 %2 = OpTypeVoid
381 %3 = OpTypeFunction %2
382 %6 = OpTypeInt 32 1
383 %7 = OpTypePointer Function %6
384 %9 = OpTypeFloat 32
385 %10 = OpTypeStruct %6 %9
386 %11 = OpTypePointer PushConstant %10
387 %12 = OpVariable %11 PushConstant
388 %13 = OpConstant %6 0
389 %14 = OpTypePointer PushConstant %6
390 %17 = OpConstant %6 1
391 %18 = OpTypePointer PushConstant %9
392 %23 = OpTypeStruct %9
393 %24 = OpTypePointer UniformConstant %23
394 %25 = OpVariable %24 UniformConstant
395 %26 = OpTypePointer UniformConstant %9
396 %50 = OpConstant %9 0
397 %4 = OpFunction %2 None %3
398 %5 = OpLabel
399 %15 = OpAccessChain %14 %12 %13
400 %19 = OpAccessChain %18 %12 %17
401 %27 = OpAccessChain %26 %25 %13
402 OpReturn
403 OpFunctionEnd
404 )";
405
406 const auto env = SPV_ENV_UNIVERSAL_1_3;
407 const auto consumer = nullptr;
408 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
409 spvtools::ValidatorOptions validator_options;
410 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
411 kConsoleMessageConsumer));
412 TransformationContext transformation_context(
413 MakeUnique<FactManager>(context.get()), validator_options);
414 transformation_context.GetFactManager()->AddFactBlockIsDead(5);
415
416 ASSERT_FALSE(
417 TransformationStore(15, 13, MakeInstructionDescriptor(27, SpvOpReturn, 0))
418 .IsApplicable(context.get(), transformation_context));
419 ASSERT_FALSE(
420 TransformationStore(19, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0))
421 .IsApplicable(context.get(), transformation_context));
422 ASSERT_FALSE(
423 TransformationStore(27, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0))
424 .IsApplicable(context.get(), transformation_context));
425 }
426
427 } // namespace
428 } // namespace fuzz
429 } // namespace spvtools
430