Fix fuzzer-discovered error with inlining.
In http://review.skia.org/375776, an optimization was added to the
Inliner, causing it to skip generation of unnecessary temporary
variables. The fuzzer immediately discovered a flaw in this logic: the
"unnecessary" variable was actually used in the rare case that a
function failed to actually return a value. The inliner didn't detect
this case. Of course, this isn't a valid program either, so now we
report the error and cleanly fail.
Change-Id: I1f201cfd33f45cace3be93765a4e214e43a46e69
Bug: oss-fuzz:31469, oss-fuzz:31525
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/377101
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
diff --git a/gn/sksl_tests.gni b/gn/sksl_tests.gni
index fefbb09..5f9c3ab 100644
--- a/gn/sksl_tests.gni
+++ b/gn/sksl_tests.gni
@@ -144,6 +144,7 @@
"/sksl/errors/Ossfuzz29845.sksl",
"/sksl/errors/Ossfuzz29849.sksl",
"/sksl/errors/Ossfuzz31410.sksl",
+ "/sksl/errors/Ossfuzz31469.sksl",
"/sksl/errors/OverflowFloatLiteral.sksl",
"/sksl/errors/OverflowIntLiteral.sksl",
"/sksl/errors/OverflowParamArraySize.sksl",
diff --git a/resources/sksl/errors/Ossfuzz31469.sksl b/resources/sksl/errors/Ossfuzz31469.sksl
new file mode 100644
index 0000000..bd23a0c
--- /dev/null
+++ b/resources/sksl/errors/Ossfuzz31469.sksl
@@ -0,0 +1,3 @@
+half n() {}
+void S() { -n(); }
+void l() { S(); }
diff --git a/src/sksl/SkSLInliner.cpp b/src/sksl/SkSLInliner.cpp
index e6e55bb..86db614 100644
--- a/src/sksl/SkSLInliner.cpp
+++ b/src/sksl/SkSLInliner.cpp
@@ -760,15 +760,22 @@
&arguments[i]->type())));
}
- if (resultExpr != nullptr) {
- // Return our result variable as our replacement expression.
+ if (resultExpr) {
+ // Return our result expression as-is.
inlinedCall.fReplacementExpr = std::move(resultExpr);
- } else {
+ } else if (function.declaration().returnType() == *fContext->fTypes.fVoid) {
// It's a void function, so it doesn't actually result in anything, but we have to return
// something non-null as a standin.
inlinedCall.fReplacementExpr = std::make_unique<BoolLiteral>(*fContext,
offset,
/*value=*/false);
+ } else {
+ // It's a non-void function, but it never created a result expression--that is, it never
+ // returned anything! Discard our output and generate an error.
+ fContext->fErrors.error(function.fOffset, String("function '") +
+ function.declaration().name() +
+ "' exits without returning a value");
+ inlinedCall = {};
}
return inlinedCall;
@@ -1198,25 +1205,29 @@
// Convert the function call to its inlined equivalent.
InlinedCall inlinedCall = this->inlineCall(&funcCall, candidate.fSymbols,
&candidate.fEnclosingFunction->declaration());
- if (inlinedCall.fInlinedBody) {
- // Ensure that the inlined body has a scope if it needs one.
- this->ensureScopedBlocks(inlinedCall.fInlinedBody.get(), candidate.fParentStmt->get());
- // Add references within the inlined body
- usage->add(inlinedCall.fInlinedBody.get());
-
- // Move the enclosing statement to the end of the unscoped Block containing the inlined
- // function, then replace the enclosing statement with that Block.
- // Before:
- // fInlinedBody = Block{ stmt1, stmt2, stmt3 }
- // fEnclosingStmt = stmt4
- // After:
- // fInlinedBody = null
- // fEnclosingStmt = Block{ stmt1, stmt2, stmt3, stmt4 }
- inlinedCall.fInlinedBody->children().push_back(std::move(*candidate.fEnclosingStmt));
- *candidate.fEnclosingStmt = std::move(inlinedCall.fInlinedBody);
+ // Stop if an error was detected during the inlining process.
+ if (!inlinedCall.fInlinedBody && !inlinedCall.fReplacementExpr) {
+ break;
}
+ // Ensure that the inlined body has a scope if it needs one.
+ this->ensureScopedBlocks(inlinedCall.fInlinedBody.get(), candidate.fParentStmt->get());
+
+ // Add references within the inlined body
+ usage->add(inlinedCall.fInlinedBody.get());
+
+ // Move the enclosing statement to the end of the unscoped Block containing the inlined
+ // function, then replace the enclosing statement with that Block.
+ // Before:
+ // fInlinedBody = Block{ stmt1, stmt2, stmt3 }
+ // fEnclosingStmt = stmt4
+ // After:
+ // fInlinedBody = null
+ // fEnclosingStmt = Block{ stmt1, stmt2, stmt3, stmt4 }
+ inlinedCall.fInlinedBody->children().push_back(std::move(*candidate.fEnclosingStmt));
+ *candidate.fEnclosingStmt = std::move(inlinedCall.fInlinedBody);
+
// Replace the candidate function call with our replacement expression.
usage->replace(candidate.fCandidateExpr->get(), inlinedCall.fReplacementExpr.get());
*candidate.fCandidateExpr = std::move(inlinedCall.fReplacementExpr);
diff --git a/tests/sksl/errors/Ossfuzz31469.glsl b/tests/sksl/errors/Ossfuzz31469.glsl
new file mode 100644
index 0000000..62d9a44
--- /dev/null
+++ b/tests/sksl/errors/Ossfuzz31469.glsl
@@ -0,0 +1,5 @@
+### Compilation failed:
+
+error: 1: function 'n' can exit without returning a value
+error: 1: function 'n' exits without returning a value
+2 errors