spirv-fuzz: Disallow copying of null and undefined pointers (#3172)
If the fuzzer object-copies a pointer we would like to be able to
perform loads from the copy (and stores to it, if its value is known
not to matter). Undefined and null pointers present a problem here,
so this change disallows copying them.
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index b2ace38..f9f9969 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -226,6 +226,20 @@
// We can only make a synonym of an instruction that has a type.
return false;
}
+ auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst->type_id());
+ if (type_inst->opcode() == SpvOpTypePointer) {
+ switch (inst->opcode()) {
+ case SpvOpConstantNull:
+ case SpvOpUndef:
+ // We disallow making synonyms of null or undefined pointers. This is
+ // to provide the property that if the original shader exhibited no bad
+ // pointer accesses, the transformed shader will not either.
+ return false;
+ default:
+ break;
+ }
+ }
+
// We do not make synonyms of objects that have decorations: if the synonym is
// not decorated analogously, using the original object vs. its synonymous
// form may not be equivalent.
diff --git a/source/fuzz/transformation_copy_object.h b/source/fuzz/transformation_copy_object.h
index 3a75ac9..ac5e978 100644
--- a/source/fuzz/transformation_copy_object.h
+++ b/source/fuzz/transformation_copy_object.h
@@ -47,6 +47,8 @@
// - It must be legal to insert an OpCopyObject instruction directly
// before 'inst'.
// - |message_.object| must be available directly before 'inst'.
+ // - |message_.object| must not be a null pointer or undefined pointer (so as
+ // to make it legal to load from copied pointers).
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
diff --git a/test/fuzz/transformation_copy_object_test.cpp b/test/fuzz/transformation_copy_object_test.cpp
index b489f71..a33f58d 100644
--- a/test/fuzz/transformation_copy_object_test.cpp
+++ b/test/fuzz/transformation_copy_object_test.cpp
@@ -588,6 +588,44 @@
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
+TEST(TransformationCopyObjectTest, DoNotCopyNullOrUndefPointers) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %8 = OpConstantNull %7
+ %9 = OpUndef %7
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // Illegal to copy null.
+ ASSERT_FALSE(TransformationCopyObject(
+ 8, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100)
+ .IsApplicable(context.get(), fact_manager));
+
+ // Illegal to copy an OpUndef of pointer type.
+ ASSERT_FALSE(TransformationCopyObject(
+ 9, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100)
+ .IsApplicable(context.get(), fact_manager));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools