From 2c7d10c7707262f6ec210c8a4f43119a86cca649 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Sun, 25 Oct 2026 10:07:15 -0700 Subject: [PATCH] [FuzzExec] Give host imported functions exact types In WebAssembly with Custom Descriptors, an inexact function import has a static inexact type `(ref $t)` because at instantiation time an open type `$t` could be satisfied by a subtype. However, at runtime, every concrete function instance (including host functions) has an exact dynamic type, and when `$t` is a final type (`!isOpen()`), `GCTypeUtils::evaluateCastCheck` optimizes casts of `(ref $t)` to `(ref (exact $t))` to succeed. When `LoggingExternalInterface::getImportedFunction` was updated in #7993 to pass `import->type` instead of `import->type.getHeapType()`, host imported functions were given an inexact runtime `Literal` type, causing runtime casts of host imported functions to `(ref (exact $t))` to fail in `--fuzz-exec` after passes like `--remove-unused-brs` folded `br_on_cast` to `ref.cast`. Fix this by passing `import->type.with(Exact)` when constructing host function literals in `LoggingExternalInterface::getImportedFunction`. --- src/tools/execution-results.h | 2 +- test/lit/exec/imported-func.wast | 4 +++- test/lit/exec/imported-func.wast.second | 19 ++++++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index bd753d071a3..48bbdc03659 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -281,7 +281,7 @@ struct LoggingExternalInterface : public ShellExternalInterface { }; // Use a null instance because this is a host function. return Literal(std::make_shared(import->name, nullptr, f), - import->type); + import->type.with(Exact)); } void throwJSException() { diff --git a/test/lit/exec/imported-func.wast b/test/lit/exec/imported-func.wast index 8a0c18f2951..452958d5b89 100644 --- a/test/lit/exec/imported-func.wast +++ b/test/lit/exec/imported-func.wast @@ -25,5 +25,7 @@ ;; CHECK-NEXT: [fuzz-exec] export cast-B-exact ;; CHECK-NEXT: [trap cast error] ;; CHECK-NEXT: [fuzz-exec] export cast-C-exact +;; CHECK-NEXT: [fuzz-exec] export cast-host-A-exact +;; CHECK-NEXT: [fuzz-exec] export cast-host-B +;; CHECK-NEXT: [trap cast error] ;; CHECK-NEXT: [fuzz-exec] export last - diff --git a/test/lit/exec/imported-func.wast.second b/test/lit/exec/imported-func.wast.second index 2719654b7c6..92eb91efd12 100644 --- a/test/lit/exec/imported-func.wast.second +++ b/test/lit/exec/imported-func.wast.second @@ -8,6 +8,7 @@ ) (import "primary" "func" (func $fimport (type $A))) + (import "fuzzing-support" "log" (func $host-import (type $A))) (func $cast-A (export "cast-A") (drop @@ -57,8 +58,24 @@ ) ) + (func $cast-host-A-exact (export "cast-host-A-exact") + ;; Host functions have the exact type of the import. + (drop + (ref.cast (ref (exact $A)) + (ref.func $host-import) + ) + ) + ) + + (func $cast-host-B (export "cast-host-B") + (drop + (ref.cast (ref $B) + (ref.func $host-import) + ) + ) + ) + (func $last (export "last") ;; Just a non-trapping method to make the test output easier to read. ) ) -