Reject delegate depth overflow in TypeChecker::OnDelegate (#2841)
wat2wasm and wasm-validate on a try...delegate with depth 0xffffffff:
$ wat2wasm bad.wat -o bad.wasm # accepted
$ wasm-validate bad.wasm ; echo $? # 0
Spotted wasm-validate passing a module that wasm-interp then hangs on.
`TypeChecker::OnDelegate` resolves the delegate target with
`GetLabel(depth + 1)`, as the depth counts from after the try being
closed. `depth` is a u32 taken straight from the module. For `depth ==
0xffffffff` the `depth + 1` wraps to 0, and `GetLabel(0)` always
succeeds, so the bounds check never fires. `delegate 0xfffffffe` is
still rejected ("invalid depth: 4294967295 (max 2)"); only the exact
wrap slips through.
Downstream the interp builds the handler from `GetNearestTryLabel(depth
+ 1)`, i.e. `GetNearestTryLabel(0)`, which returns the try being closed.
The try delegates to itself, and `Thread::DoThrow` loops on the
self-referential handler.
Rejecting `depth == kInvalidIndex` before the add closes both the wat
and binary paths. Every in-range delegate is untouched.diff --git a/src/type-checker.cc b/src/type-checker.cc
index ada0ab0..8bc8e63 100644
--- a/src/type-checker.cc
+++ b/src/type-checker.cc
@@ -772,7 +772,12 @@
Result result = Result::Ok;
Label* label;
// Delegate starts counting after the current try, as the delegate
- // instruction is not actually in the try block.
+ // instruction is not actually in the try block. depth + 1 wraps to 0 when
+ // depth is kInvalidIndex, which would slip past the bounds check in GetLabel.
+ if (depth == kInvalidIndex) {
+ PrintError("invalid depth: %" PRIindex, depth);
+ return Result::Error;
+ }
CHECK_RESULT(GetLabel(depth + 1, &label));
Label* try_label;
diff --git a/test/typecheck/bad-delegate-depth-overflow.txt b/test/typecheck/bad-delegate-depth-overflow.txt
new file mode 100644
index 0000000..eca61e3
--- /dev/null
+++ b/test/typecheck/bad-delegate-depth-overflow.txt
@@ -0,0 +1,16 @@
+;;; TOOL: wat2wasm
+;;; ERROR: 1
+(module
+ (tag $e)
+ (func
+ try
+ try
+ throw $e
+ delegate 4294967295
+ catch $e
+ end))
+(;; STDERR ;;;
+out/test/typecheck/bad-delegate-depth-overflow.txt:7:7: error: invalid depth: 4294967295
+ try
+ ^^^
+;;; STDERR ;;)