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