summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cfg/cfg-traversal.h3
-rw-r--r--src/wasm/wasm-validator.cpp32
-rw-r--r--test/lit/validation/nn-locals-bad-call_ref.wast31
-rw-r--r--test/lit/validation/nn-locals-bad.wast15
-rw-r--r--test/lit/validation/nn-locals-ok.wast14
5 files changed, 94 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,
diff --git a/test/lit/validation/nn-locals-bad-call_ref.wast b/test/lit/validation/nn-locals-bad-call_ref.wast
new file mode 100644
index 000000000..357409eef
--- /dev/null
+++ b/test/lit/validation/nn-locals-bad-call_ref.wast
@@ -0,0 +1,31 @@
+;; Test for validation of non-nullable locals
+
+;; RUN: not wasm-opt -all --enable-gc-nn-locals %s 2>&1 | filecheck %s
+
+;; CHECK: non-nullable local must not read null
+
+(module
+ (tag $tag (param i32))
+ (func $func
+ (local $0 (ref any))
+ (try
+ (do
+ (call_ref
+ (ref.func $func)
+ )
+ )
+ (catch $tag
+ (drop
+ (pop (i32))
+ )
+ ;; The path to here is from a possible exception thrown in the call_ref.
+ ;; This is a regression test for call_ref not being seen as possibly
+ ;; throwing. We should see a validation error here, as the local.get is
+ ;; of a null default, and it *is* reachable thanks to the call_ref.
+ (drop
+ (local.get $0)
+ )
+ )
+ )
+ )
+)
diff --git a/test/lit/validation/nn-locals-bad.wast b/test/lit/validation/nn-locals-bad.wast
new file mode 100644
index 000000000..17a5c1404
--- /dev/null
+++ b/test/lit/validation/nn-locals-bad.wast
@@ -0,0 +1,15 @@
+;; Test for validation of non-nullable locals
+
+;; RUN: not wasm-opt -all --enable-gc-nn-locals %s 2>&1 | filecheck %s
+
+;; CHECK: non-nullable local must not read null
+
+(module
+ (func $foo
+ (local $nn (ref any))
+ ;; It is not ok to read a non-nullable local.
+ (drop
+ (local.get $nn)
+ )
+ )
+)
diff --git a/test/lit/validation/nn-locals-ok.wast b/test/lit/validation/nn-locals-ok.wast
new file mode 100644
index 000000000..1dfd20962
--- /dev/null
+++ b/test/lit/validation/nn-locals-ok.wast
@@ -0,0 +1,14 @@
+;; Test for validation of non-nullable locals
+
+;; RUN: wasm-opt -all --enable-gc-nn-locals %s --print | filecheck %s
+
+;; CHECK: (module
+
+(module
+ (func $foo (param $nn (ref any))
+ ;; It is ok to read a non-nullable param.
+ (drop
+ (local.get $nn)
+ )
+ )
+)