spirv-fuzz: Check termination instructions when donating modules (#3710)
The FuzzerPassDonateModules was not checking if the function to donate had
a block with OpKill or OpUnreachable as its termination instruction.
Fixes #3709.
diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp
index a8b7ddc..f54d3e4 100644
--- a/source/fuzz/fuzzer_pass_donate_modules.cpp
+++ b/source/fuzz/fuzzer_pass_donate_modules.cpp
@@ -1234,23 +1234,19 @@
}
}
- // If the function contains OpKill or OpUnreachable instructions, and has
- // non-void return type, then we need a value %v to use in order to turn
- // these into instructions of the form OpReturn %v.
- uint32_t kill_unreachable_return_value_id;
+ // If |function_to_donate| has non-void return type and contains an
+ // OpKill/OpUnreachable instruction, then a value is needed in order to turn
+ // these into instructions of the form OpReturnValue %value_id.
+ uint32_t kill_unreachable_return_value_id = 0;
auto function_return_type_inst =
donor_ir_context->get_def_use_mgr()->GetDef(function_to_donate.type_id());
- if (function_return_type_inst->opcode() == SpvOpTypeVoid) {
- // The return type is void, so we don't need a return value.
- kill_unreachable_return_value_id = 0;
- } else {
- // We do need a return value; we use zero.
- assert(function_return_type_inst->opcode() != SpvOpTypePointer &&
- "Function return type must not be a pointer.");
+ if (function_return_type_inst->opcode() != SpvOpTypeVoid &&
+ fuzzerutil::FunctionContainsOpKillOrUnreachable(function_to_donate)) {
kill_unreachable_return_value_id = FindOrCreateZeroConstant(
original_id_to_donated_id.at(function_return_type_inst->result_id()),
false);
}
+
// Add the function in a livesafe manner.
ApplyTransformation(TransformationAddFunction(
donated_instructions, loop_limiter_variable_id, loop_limit, loop_limiters,
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index f9564bc..9e5b30e 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -476,6 +476,16 @@
return nullptr;
}
+bool FunctionContainsOpKillOrUnreachable(const opt::Function& function) {
+ for (auto& block : function) {
+ if (block.terminator()->opcode() == SpvOpKill ||
+ block.terminator()->opcode() == SpvOpUnreachable) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id) {
for (auto& entry_point : context->module()->entry_points()) {
if (entry_point.GetSingleWordInOperand(1) == function_id) {
diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index cb01ca7..d86e138 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -179,6 +179,10 @@
// function exists.
opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id);
+// Returns true if |function| has a block that the termination instruction is
+// OpKill or OpUnreachable.
+bool FunctionContainsOpKillOrUnreachable(const opt::Function& function);
+
// Returns |true| if one of entry points has function id |function_id|.
bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id);