summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2022-05-25 10:09:57 -0700
committerGitHub <noreply@github.com>2022-05-25 17:09:57 +0000
commit808be8a27f7adf00f2410219507b649cdab4aa99 (patch)
tree504dc7d13929ad22137e7716043afdb4331e7599 /src
parent3542bd9429529bfd9fbe6a85f8f97a9388ef3c2a (diff)
downloadbinaryen-808be8a27f7adf00f2410219507b649cdab4aa99.tar.gz
binaryen-808be8a27f7adf00f2410219507b649cdab4aa99.tar.bz2
binaryen-808be8a27f7adf00f2410219507b649cdab4aa99.zip
[Wasm GC] Fix CFG traversal of call_ref and add missing validation check (#4690)
We were missing CallRef in the CFG traversal code in a place where we note possible exceptions. As a result we thought CallRef cannot throw, and were missing some control flow edges. To actually detect the problem, we need to validate non-nullable locals properly, which we were not doing. This adds that as well.
Diffstat (limited to 'src')
-rw-r--r--src/cfg/cfg-traversal.h3
-rw-r--r--src/wasm/wasm-validator.cpp32
2 files changed, 34 insertions, 1 deletions
diff --git a/src/cfg/cfg-traversal.h b/src/cfg/cfg-traversal.h
index faaa3c830..478b4780e 100644
--- a/src/cfg/cfg-traversal.h
+++ b/src/cfg/cfg-traversal.h
@@ -392,7 +392,8 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> {
break;
}
case Expression::Id::CallId:
- case Expression::Id::CallIndirectId: {
+ case Expression::Id::CallIndirectId:
+ case Expression::Id::CallRefId: {
self->pushTask(SubType::doEndCall, currp);
break;
}
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index c74d95fdd..9845da72c 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -23,6 +23,7 @@
#include "ir/features.h"
#include "ir/global-utils.h"
#include "ir/intrinsics.h"
+#include "ir/local-graph.h"
#include "ir/module-utils.h"
#include "ir/stack-utils.h"
#include "ir/utils.h"
@@ -2779,6 +2780,37 @@ void FunctionValidator::visitFunction(Function* curr) {
Name name = pair.second;
shouldBeTrue(seen.insert(name).second, name, "local names must be unique");
}
+
+ if (getModule()->features.hasGCNNLocals()) {
+ // If we have non-nullable locals, verify that no local.get can read a null
+ // default value.
+ // TODO: this can be fairly slow due to the LocalGraph. writing more code to
+ // do a more specific analysis (we don't need to know all sets, just
+ // if there is a set of a null default value that is read) could be a
+ // lot faster.
+ bool hasNNLocals = false;
+ for (const auto& var : curr->vars) {
+ if (var.isNonNullable()) {
+ hasNNLocals = true;
+ break;
+ }
+ }
+ if (hasNNLocals) {
+ LocalGraph graph(curr);
+ for (auto& [get, sets] : graph.getSetses) {
+ auto index = get->index;
+ // It is always ok to read nullable locals, and it is always ok to read
+ // params even if they are non-nullable.
+ if (!curr->getLocalType(index).isNonNullable() ||
+ curr->isParam(index)) {
+ continue;
+ }
+ for (auto* set : sets) {
+ shouldBeTrue(!!set, index, "non-nullable local must not read null");
+ }
+ }
+ }
+ }
}
static bool checkSegmentOffset(Expression* curr,