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_function_call.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(TransformationFunctionCallTest, BasicTest)26 TEST(TransformationFunctionCallTest, 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 = OpTypePointer Function %6
38 %8 = OpTypeFunction %6 %7
39 %12 = OpTypeFloat 32
40 %13 = OpTypePointer Function %12
41 %14 = OpTypeFunction %6 %7 %13
42 %27 = OpConstant %6 1
43 %50 = OpConstant %12 1
44 %57 = OpTypeBool
45 %58 = OpConstantFalse %57
46 %204 = OpUndef %6
47 %4 = OpFunction %2 None %3
48 %5 = OpLabel
49 %61 = OpVariable %7 Function
50 %62 = OpVariable %7 Function
51 %65 = OpVariable %13 Function
52 %66 = OpVariable %7 Function
53 %68 = OpVariable %13 Function
54 %71 = OpVariable %7 Function
55 %72 = OpVariable %13 Function
56 %73 = OpVariable %7 Function
57 %75 = OpVariable %13 Function
58 %78 = OpVariable %7 Function
59 %98 = OpAccessChain %7 %71
60 %99 = OpCopyObject %7 %71
61 OpSelectionMerge %60 None
62 OpBranchConditional %58 %59 %60
63 %59 = OpLabel
64 OpBranch %60
65 %60 = OpLabel
66 OpReturn
67 OpFunctionEnd
68 %10 = OpFunction %6 None %8
69 %9 = OpFunctionParameter %7
70 %11 = OpLabel
71 %26 = OpLoad %6 %9
72 %28 = OpIAdd %6 %26 %27
73 OpSelectionMerge %97 None
74 OpBranchConditional %58 %96 %97
75 %96 = OpLabel
76 OpBranch %97
77 %97 = OpLabel
78 OpReturnValue %28
79 OpFunctionEnd
80 %17 = OpFunction %6 None %14
81 %15 = OpFunctionParameter %7
82 %16 = OpFunctionParameter %13
83 %18 = OpLabel
84 %31 = OpVariable %7 Function
85 %32 = OpLoad %6 %15
86 OpStore %31 %32
87 %33 = OpFunctionCall %6 %10 %31
88 OpReturnValue %33
89 OpFunctionEnd
90 %21 = OpFunction %6 None %14
91 %19 = OpFunctionParameter %7
92 %20 = OpFunctionParameter %13
93 %22 = OpLabel
94 %36 = OpLoad %6 %19
95 %37 = OpLoad %12 %20
96 %38 = OpConvertFToS %6 %37
97 %39 = OpIAdd %6 %36 %38
98 OpReturnValue %39
99 OpFunctionEnd
100 %24 = OpFunction %6 None %8
101 %23 = OpFunctionParameter %7
102 %25 = OpLabel
103 %44 = OpVariable %7 Function
104 %46 = OpVariable %13 Function
105 %51 = OpVariable %7 Function
106 %52 = OpVariable %13 Function
107 %42 = OpLoad %6 %23
108 %43 = OpConvertSToF %12 %42
109 %45 = OpLoad %6 %23
110 OpStore %44 %45
111 OpStore %46 %43
112 %47 = OpFunctionCall %6 %17 %44 %46
113 %48 = OpLoad %6 %23
114 %49 = OpIAdd %6 %48 %27
115 OpStore %51 %49
116 OpStore %52 %50
117 %53 = OpFunctionCall %6 %17 %51 %52
118 %54 = OpIAdd %6 %47 %53
119 OpReturnValue %54
120 OpFunctionEnd
121 %200 = OpFunction %6 None %14
122 %201 = OpFunctionParameter %7
123 %202 = OpFunctionParameter %13
124 %203 = OpLabel
125 OpSelectionMerge %206 None
126 OpBranchConditional %58 %205 %206
127 %205 = OpLabel
128 OpBranch %206
129 %206 = OpLabel
130 OpReturnValue %204
131 OpFunctionEnd
132 )";
133
134 const auto env = SPV_ENV_UNIVERSAL_1_4;
135 const auto consumer = nullptr;
136 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
137 spvtools::ValidatorOptions validator_options;
138 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
139 kConsoleMessageConsumer));
140 TransformationContext transformation_context(
141 MakeUnique<FactManager>(context.get()), validator_options);
142 transformation_context.GetFactManager()->AddFactBlockIsDead(59);
143 transformation_context.GetFactManager()->AddFactBlockIsDead(11);
144 transformation_context.GetFactManager()->AddFactBlockIsDead(18);
145 transformation_context.GetFactManager()->AddFactBlockIsDead(25);
146 transformation_context.GetFactManager()->AddFactBlockIsDead(96);
147 transformation_context.GetFactManager()->AddFactBlockIsDead(205);
148 transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(21);
149 transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(200);
150 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
151 71);
152 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
153 72);
154 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
155 19);
156 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
157 20);
158 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
159 23);
160 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
161 44);
162 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
163 46);
164 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
165 51);
166 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
167 52);
168
169 // Livesafe functions with argument types: 21(7, 13), 200(7, 13)
170 // Non-livesafe functions with argument types: 4(), 10(7), 17(7, 13), 24(7)
171 // Call graph edges:
172 // 17 -> 10
173 // 24 -> 17
174
175 // Bad transformations
176 // Too many arguments
177 ASSERT_FALSE(TransformationFunctionCall(
178 100, 21, {71, 72, 71},
179 MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
180 .IsApplicable(context.get(), transformation_context));
181 // Too few arguments
182 ASSERT_FALSE(
183 TransformationFunctionCall(
184 100, 21, {71}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
185 .IsApplicable(context.get(), transformation_context));
186 // Arguments are the wrong way around (types do not match)
187 ASSERT_FALSE(TransformationFunctionCall(
188 100, 21, {72, 71},
189 MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
190 .IsApplicable(context.get(), transformation_context));
191 // 21 is not an appropriate argument
192 ASSERT_FALSE(TransformationFunctionCall(
193 100, 21, {21, 72},
194 MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
195 .IsApplicable(context.get(), transformation_context));
196 // 300 does not exist
197 ASSERT_FALSE(TransformationFunctionCall(
198 100, 21, {300, 72},
199 MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
200 .IsApplicable(context.get(), transformation_context));
201 // 71 is not a function
202 ASSERT_FALSE(TransformationFunctionCall(
203 100, 71, {71, 72},
204 MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
205 .IsApplicable(context.get(), transformation_context));
206 // 500 does not exist
207 ASSERT_FALSE(TransformationFunctionCall(
208 100, 500, {71, 72},
209 MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
210 .IsApplicable(context.get(), transformation_context));
211 // Id is not fresh
212 ASSERT_FALSE(
213 TransformationFunctionCall(
214 21, 21, {71, 72}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
215 .IsApplicable(context.get(), transformation_context));
216 // Access chain as pointer parameter
217 ASSERT_FALSE(TransformationFunctionCall(
218 100, 21, {98, 72},
219 MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
220 .IsApplicable(context.get(), transformation_context));
221 // Copied object as pointer parameter
222 ASSERT_FALSE(TransformationFunctionCall(
223 100, 21, {99, 72},
224 MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
225 .IsApplicable(context.get(), transformation_context));
226 // Non-livesafe called from original live block
227 ASSERT_FALSE(TransformationFunctionCall(
228 100, 10, {71},
229 MakeInstructionDescriptor(99, spv::Op::OpSelectionMerge, 0))
230 .IsApplicable(context.get(), transformation_context));
231 // Non-livesafe called from livesafe function
232 ASSERT_FALSE(TransformationFunctionCall(
233 100, 10, {19},
234 MakeInstructionDescriptor(38, spv::Op::OpConvertFToS, 0))
235 .IsApplicable(context.get(), transformation_context));
236 // Livesafe function called with pointer to non-arbitrary local variable
237 ASSERT_FALSE(TransformationFunctionCall(
238 100, 21, {61, 72},
239 MakeInstructionDescriptor(38, spv::Op::OpConvertFToS, 0))
240 .IsApplicable(context.get(), transformation_context));
241 // Direct recursion
242 ASSERT_FALSE(
243 TransformationFunctionCall(
244 100, 4, {}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
245 .IsApplicable(context.get(), transformation_context));
246 // Indirect recursion
247 ASSERT_FALSE(
248 TransformationFunctionCall(
249 100, 24, {9}, MakeInstructionDescriptor(96, spv::Op::OpBranch, 0))
250 .IsApplicable(context.get(), transformation_context));
251 // Parameter 23 is not available at the call site
252 ASSERT_FALSE(
253 TransformationFunctionCall(
254 104, 10, {23}, MakeInstructionDescriptor(205, spv::Op::OpBranch, 0))
255 .IsApplicable(context.get(), transformation_context));
256
257 // Good transformations
258 {
259 // Livesafe called from dead block: fine
260 TransformationFunctionCall transformation(
261 100, 21, {71, 72}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0));
262 ASSERT_TRUE(
263 transformation.IsApplicable(context.get(), transformation_context));
264 ApplyAndCheckFreshIds(transformation, context.get(),
265 &transformation_context);
266 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
267 context.get(), validator_options, kConsoleMessageConsumer));
268 }
269 {
270 // Livesafe called from original live block: fine
271 TransformationFunctionCall transformation(
272 101, 21, {71, 72},
273 MakeInstructionDescriptor(98, spv::Op::OpAccessChain, 0));
274 ASSERT_TRUE(
275 transformation.IsApplicable(context.get(), transformation_context));
276 ApplyAndCheckFreshIds(transformation, context.get(),
277 &transformation_context);
278 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
279 context.get(), validator_options, kConsoleMessageConsumer));
280 }
281 {
282 // Livesafe called from livesafe function: fine
283 TransformationFunctionCall transformation(
284 102, 200, {19, 20}, MakeInstructionDescriptor(36, spv::Op::OpLoad, 0));
285 ASSERT_TRUE(
286 transformation.IsApplicable(context.get(), transformation_context));
287 ApplyAndCheckFreshIds(transformation, context.get(),
288 &transformation_context);
289 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
290 context.get(), validator_options, kConsoleMessageConsumer));
291 }
292 {
293 // Dead called from dead block in injected function: fine
294 TransformationFunctionCall transformation(
295 103, 10, {23}, MakeInstructionDescriptor(45, spv::Op::OpLoad, 0));
296 ASSERT_TRUE(
297 transformation.IsApplicable(context.get(), transformation_context));
298 ApplyAndCheckFreshIds(transformation, context.get(),
299 &transformation_context);
300 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
301 context.get(), validator_options, kConsoleMessageConsumer));
302 }
303 {
304 // Non-livesafe called from dead block in livesafe function: OK
305 TransformationFunctionCall transformation(
306 104, 10, {201}, MakeInstructionDescriptor(205, spv::Op::OpBranch, 0));
307 ASSERT_TRUE(
308 transformation.IsApplicable(context.get(), transformation_context));
309 ApplyAndCheckFreshIds(transformation, context.get(),
310 &transformation_context);
311 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
312 context.get(), validator_options, kConsoleMessageConsumer));
313 }
314 {
315 // Livesafe called from dead block with non-arbitrary parameter
316 TransformationFunctionCall transformation(
317 105, 21, {62, 65}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0));
318 ASSERT_TRUE(
319 transformation.IsApplicable(context.get(), transformation_context));
320 ApplyAndCheckFreshIds(transformation, context.get(),
321 &transformation_context);
322 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
323 context.get(), validator_options, kConsoleMessageConsumer));
324 }
325
326 std::string after_transformation = R"(
327 OpCapability Shader
328 %1 = OpExtInstImport "GLSL.std.450"
329 OpMemoryModel Logical GLSL450
330 OpEntryPoint Fragment %4 "main"
331 OpExecutionMode %4 OriginUpperLeft
332 OpSource ESSL 310
333 %2 = OpTypeVoid
334 %3 = OpTypeFunction %2
335 %6 = OpTypeInt 32 1
336 %7 = OpTypePointer Function %6
337 %8 = OpTypeFunction %6 %7
338 %12 = OpTypeFloat 32
339 %13 = OpTypePointer Function %12
340 %14 = OpTypeFunction %6 %7 %13
341 %27 = OpConstant %6 1
342 %50 = OpConstant %12 1
343 %57 = OpTypeBool
344 %58 = OpConstantFalse %57
345 %204 = OpUndef %6
346 %4 = OpFunction %2 None %3
347 %5 = OpLabel
348 %61 = OpVariable %7 Function
349 %62 = OpVariable %7 Function
350 %65 = OpVariable %13 Function
351 %66 = OpVariable %7 Function
352 %68 = OpVariable %13 Function
353 %71 = OpVariable %7 Function
354 %72 = OpVariable %13 Function
355 %73 = OpVariable %7 Function
356 %75 = OpVariable %13 Function
357 %78 = OpVariable %7 Function
358 %101 = OpFunctionCall %6 %21 %71 %72
359 %98 = OpAccessChain %7 %71
360 %99 = OpCopyObject %7 %71
361 OpSelectionMerge %60 None
362 OpBranchConditional %58 %59 %60
363 %59 = OpLabel
364 %100 = OpFunctionCall %6 %21 %71 %72
365 %105 = OpFunctionCall %6 %21 %62 %65
366 OpBranch %60
367 %60 = OpLabel
368 OpReturn
369 OpFunctionEnd
370 %10 = OpFunction %6 None %8
371 %9 = OpFunctionParameter %7
372 %11 = OpLabel
373 %26 = OpLoad %6 %9
374 %28 = OpIAdd %6 %26 %27
375 OpSelectionMerge %97 None
376 OpBranchConditional %58 %96 %97
377 %96 = OpLabel
378 OpBranch %97
379 %97 = OpLabel
380 OpReturnValue %28
381 OpFunctionEnd
382 %17 = OpFunction %6 None %14
383 %15 = OpFunctionParameter %7
384 %16 = OpFunctionParameter %13
385 %18 = OpLabel
386 %31 = OpVariable %7 Function
387 %32 = OpLoad %6 %15
388 OpStore %31 %32
389 %33 = OpFunctionCall %6 %10 %31
390 OpReturnValue %33
391 OpFunctionEnd
392 %21 = OpFunction %6 None %14
393 %19 = OpFunctionParameter %7
394 %20 = OpFunctionParameter %13
395 %22 = OpLabel
396 %102 = OpFunctionCall %6 %200 %19 %20
397 %36 = OpLoad %6 %19
398 %37 = OpLoad %12 %20
399 %38 = OpConvertFToS %6 %37
400 %39 = OpIAdd %6 %36 %38
401 OpReturnValue %39
402 OpFunctionEnd
403 %24 = OpFunction %6 None %8
404 %23 = OpFunctionParameter %7
405 %25 = OpLabel
406 %44 = OpVariable %7 Function
407 %46 = OpVariable %13 Function
408 %51 = OpVariable %7 Function
409 %52 = OpVariable %13 Function
410 %42 = OpLoad %6 %23
411 %43 = OpConvertSToF %12 %42
412 %103 = OpFunctionCall %6 %10 %23
413 %45 = OpLoad %6 %23
414 OpStore %44 %45
415 OpStore %46 %43
416 %47 = OpFunctionCall %6 %17 %44 %46
417 %48 = OpLoad %6 %23
418 %49 = OpIAdd %6 %48 %27
419 OpStore %51 %49
420 OpStore %52 %50
421 %53 = OpFunctionCall %6 %17 %51 %52
422 %54 = OpIAdd %6 %47 %53
423 OpReturnValue %54
424 OpFunctionEnd
425 %200 = OpFunction %6 None %14
426 %201 = OpFunctionParameter %7
427 %202 = OpFunctionParameter %13
428 %203 = OpLabel
429 OpSelectionMerge %206 None
430 OpBranchConditional %58 %205 %206
431 %205 = OpLabel
432 %104 = OpFunctionCall %6 %10 %201
433 OpBranch %206
434 %206 = OpLabel
435 OpReturnValue %204
436 OpFunctionEnd
437 )";
438 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
439 }
440
TEST(TransformationFunctionCallTest, DoNotInvokeEntryPoint)441 TEST(TransformationFunctionCallTest, DoNotInvokeEntryPoint) {
442 std::string shader = R"(
443 OpCapability Shader
444 %1 = OpExtInstImport "GLSL.std.450"
445 OpMemoryModel Logical GLSL450
446 OpEntryPoint Fragment %4 "main"
447 OpExecutionMode %4 OriginUpperLeft
448 OpSource ESSL 310
449 %2 = OpTypeVoid
450 %3 = OpTypeFunction %2
451 %4 = OpFunction %2 None %3
452 %5 = OpLabel
453 OpReturn
454 OpFunctionEnd
455 %10 = OpFunction %2 None %3
456 %11 = OpLabel
457 OpReturn
458 OpFunctionEnd
459 )";
460
461 const auto env = SPV_ENV_UNIVERSAL_1_4;
462 const auto consumer = nullptr;
463 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
464 spvtools::ValidatorOptions validator_options;
465 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
466 kConsoleMessageConsumer));
467 TransformationContext transformation_context(
468 MakeUnique<FactManager>(context.get()), validator_options);
469 transformation_context.GetFactManager()->AddFactBlockIsDead(11);
470
471 // 4 is an entry point, so it is not legal for it to be the target of a call.
472 ASSERT_FALSE(
473 TransformationFunctionCall(
474 100, 4, {}, MakeInstructionDescriptor(11, spv::Op::OpReturn, 0))
475 .IsApplicable(context.get(), transformation_context));
476 }
477
478 } // namespace
479 } // namespace fuzz
480 } // namespace spvtools
481