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 22namespace spvtools { 23namespace fuzz { 24namespace { 25 26TEST(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 441TEST(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