From 5d44f8fe111d545a20aa22d59dfa61c82f147576 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Wed, 11 Feb 2026 15:28:57 -0800 Subject: [PATCH 1/2] Validate the use of shared types We started validating that shared-everything is enabled when we defined shared types in #8298, but this missed the case where a non-shared type definition used a shared abstract heap type, which has no definition. Update the validation to check that the used types are allowed by the enabled feature set as well. Refactor the validation logic into several functions to avoid duplication of logic. --- src/wasm/wasm-type.cpp | 113 ++++++++++++------ .../validation/shared-abstract-heap-type.wast | 12 ++ 2 files changed, 90 insertions(+), 35 deletions(-) create mode 100644 test/lit/validation/shared-abstract-heap-type.wast diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index d621df72154..83c58593010 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -2426,9 +2426,66 @@ bool isValidSupertype(const HeapTypeInfo& sub, const HeapTypeInfo& super) { } std::optional -validateType(HeapTypeInfo& info, - std::unordered_set& seenTypes, - FeatureSet features) { +validateType(Type type, FeatureSet feats, bool isShared) { + if (type.isRef()) { + auto heapType = type.getHeapType(); + if (isShared && !heapType.isShared()) { + return TypeBuilder::ErrorReason::InvalidUnsharedField; + } + if (heapType.isShared() && !feats.hasSharedEverything()) { + return TypeBuilder::ErrorReason::InvalidSharedType; + } + } + return std::nullopt; +} + +std::optional +validateStruct(const Struct& struct_, FeatureSet feats, bool isShared) { + for (auto& field : struct_.fields) { + if (auto err = validateType(field.type, feats, isShared)) { + return err; + } + } + return std::nullopt; +} + +std::optional +validateArray(Array array, FeatureSet feats, bool isShared) { + return validateType(array.element.type, feats, isShared); +} + +std::optional +validateSignature(Signature sig, FeatureSet feats, bool isShared) { + // Allow unshared parameters and results even in shared functions. + // TODO: Figure out and enforce shared function rules. + for (auto t : sig.params) { + if (auto err = validateType(t, feats, /*isShared=*/false)) { + return err; + } + } + for (auto t : sig.results) { + if (auto err = validateType(t, feats, /*isShared*/ false)) { + return err; + } + } + return std::nullopt; +} + +std::optional +validateContinuation(Continuation cont, FeatureSet feats, bool isShared) { + if (!cont.type.isSignature()) { + return TypeBuilder::ErrorReason::InvalidFuncType; + } + if (isShared != cont.type.isShared()) { + return TypeBuilder::ErrorReason::InvalidFuncType; + } + return std::nullopt; +} + +std::optional +validateTypeInfo(HeapTypeInfo& info, + std::unordered_set& seenTypes, + FeatureSet features) { if (auto* super = info.supertype) { // The supertype must be canonical (i.e. defined in a previous rec group) // or have already been defined in this rec group. @@ -2466,11 +2523,6 @@ validateType(HeapTypeInfo& info, return TypeBuilder::ErrorReason::MismatchedDescriptor; } } - if (info.isContinuation()) { - if (!info.continuation.type.isSignature()) { - return TypeBuilder::ErrorReason::InvalidFuncType; - } - } if (info.share == Shared) { if (!features.hasSharedEverything()) { return TypeBuilder::ErrorReason::InvalidSharedType; @@ -2481,32 +2533,23 @@ validateType(HeapTypeInfo& info, if (info.descriptor && info.descriptor->share != Shared) { return TypeBuilder::ErrorReason::InvalidUnsharedDescriptor; } - switch (info.kind) { - case HeapTypeKind::Func: - // TODO: Figure out and enforce shared function rules. - break; - case HeapTypeKind::Cont: - if (!info.continuation.type.isShared()) { - return TypeBuilder::ErrorReason::InvalidFuncType; - } - break; - case HeapTypeKind::Struct: - for (auto& field : info.struct_.fields) { - if (field.type.isRef() && !field.type.getHeapType().isShared()) { - return TypeBuilder::ErrorReason::InvalidUnsharedField; - } - } - break; - case HeapTypeKind::Array: { - auto elem = info.array.element.type; - if (elem.isRef() && !elem.getHeapType().isShared()) { - return TypeBuilder::ErrorReason::InvalidUnsharedField; - } - break; - } - case HeapTypeKind::Basic: - WASM_UNREACHABLE("unexpected kind"); - } + } + bool isShared = info.share == Shared; + switch (info.kind) { + case HeapTypeKind::Func: + return validateSignature(info.signature, features, isShared); + break; + case HeapTypeKind::Cont: + return validateContinuation(info.continuation, features, isShared); + break; + case HeapTypeKind::Struct: + return validateStruct(info.struct_, features, isShared); + break; + case HeapTypeKind::Array: + return validateArray(info.array, features, isShared); + break; + case HeapTypeKind::Basic: + WASM_UNREACHABLE("unexpected kind"); } return std::nullopt; } @@ -2590,7 +2633,7 @@ buildRecGroup(std::unique_ptr&& groupInfo, std::unordered_set seenTypes; for (size_t i = 0; i < typeInfos.size(); ++i) { auto& info = typeInfos[i]; - if (auto err = validateType(*info, seenTypes, features)) { + if (auto err = validateTypeInfo(*info, seenTypes, features)) { return {TypeBuilder::Error{i, *err}}; } seenTypes.insert(asHeapType(info)); diff --git a/test/lit/validation/shared-abstract-heap-type.wast b/test/lit/validation/shared-abstract-heap-type.wast new file mode 100644 index 00000000000..ba96dbeb2b3 --- /dev/null +++ b/test/lit/validation/shared-abstract-heap-type.wast @@ -0,0 +1,12 @@ +;; Test that shared abstract heap types require shared-everything threads + +;; RUN: not wasm-opt %s 2>&1 | filecheck %s --check-prefix NO-SHARED +;; RUN: wasm-opt %s --enable-reference-types --enable-gc --enable-shared-everything -o - -S | filecheck %s --check-prefix SHARED + +;; NO-SHARED: invalid type: Shared types require shared-everything +;; SHARED: (type $t (struct (field (ref null (shared any))))) + +(module + (type $t (struct (field (ref null (shared any))))) + (global (ref null $t) (ref.null none)) +) From e3a9bc6f68d33939e6a9fffe6a0d7461a8b274d1 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Wed, 11 Feb 2026 16:34:04 -0800 Subject: [PATCH 2/2] update fuzztest type generator --- test/gtest/type-domains.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gtest/type-domains.cpp b/test/gtest/type-domains.cpp index ba3f4906f84..c3f50a2516b 100644 --- a/test/gtest/type-domains.cpp +++ b/test/gtest/type-domains.cpp @@ -854,7 +854,7 @@ fuzztest::Domain ContDef(TypeBuilderPlan plan) { bool shared = plan.kinds[plan.curr].shared; auto matches = AvailableMatchingIndices(std::move(plan), [&](auto kind, bool otherShared) { - return kind == FuncKind && (!shared || otherShared); + return kind == FuncKind && shared == otherShared; }); if (matches.empty()) { return fuzztest::NullOpt();