diff options
-rw-r--r-- | src/ir/struct-utils.h | 4 | ||||
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 5 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 9 | ||||
-rw-r--r-- | src/tools/fuzzing/fuzzing.cpp | 62 | ||||
-rw-r--r-- | test/passes/translate-to-fuzz_all-features_metrics_noprint.txt | 56 |
5 files changed, 105 insertions, 31 deletions
diff --git a/src/ir/struct-utils.h b/src/ir/struct-utils.h index 9f880985f..dc87ed574 100644 --- a/src/ir/struct-utils.h +++ b/src/ir/struct-utils.h @@ -22,6 +22,10 @@ namespace wasm { +// A pair of a struct type and a field index, together defining a field in a +// particular type. +using StructField = std::pair<HeapType, Index>; + namespace StructUtils { // A vector of a template type's values. One such vector will be used per struct diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 228b3afc1..6842c852e 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -41,6 +41,7 @@ #include "ir/find_all.h" #include "ir/intrinsics.h" #include "ir/module-utils.h" +#include "ir/struct-utils.h" #include "ir/subtypes.h" #include "ir/utils.h" #include "pass.h" @@ -63,10 +64,6 @@ enum class ModuleElementKind { // name of the particular element. using ModuleElement = std::pair<ModuleElementKind, Name>; -// A pair of a struct type and a field index, together defining a field in a -// particular type. -using StructField = std::pair<HeapType, Index>; - // Visit or walk an expression to find what things are referenced. struct ReferenceFinder : public PostWalker<ReferenceFinder> { // Our findings are placed in these data structures, which the user of this diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index e53665e14..24f08dc53 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -29,6 +29,7 @@ high chance for set at start of loop #include "ir/branch-utils.h" #include "ir/memory-utils.h" +#include "ir/struct-utils.h" #include "support/insert_ordered.h" #include "tools/fuzzing/random.h" #include <ir/eh-utils.h> @@ -117,6 +118,12 @@ private: // subtypes of it. std::unordered_map<HeapType, std::vector<HeapType>> interestingHeapSubTypes; + // Type => list of struct fields that have that type. + std::unordered_map<Type, std::vector<StructField>> typeStructFields; + + // Type => list of array types that have that type. + std::unordered_map<Type, std::vector<HeapType>> typeArrays; + Index numAddedFunctions = 0; // RAII helper for managing the state used to create a single function. @@ -326,6 +333,8 @@ private: Expression* makeRefEq(Type type); Expression* makeRefTest(Type type); Expression* makeRefCast(Type type); + Expression* makeStructGet(Type type); + Expression* makeArrayGet(Type type); Expression* makeI31Get(Type type); Expression* makeMemoryInit(); Expression* makeDataDrop(); diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 02902fb15..fd8722224 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -295,6 +295,18 @@ void TranslateToFuzzReader::setupHeapTypes() { interestingHeapSubTypes[HeapType::func].push_back(type); } } + + // Compute struct and array fields. + for (auto type : interestingHeapTypes) { + if (type.isStruct()) { + auto& fields = type.getStruct().fields; + for (Index i = 0; i < fields.size(); i++) { + typeStructFields[fields[i].type].push_back(StructField{type, i}); + } + } else if (type.isArray()) { + typeArrays[type.getArray().element.type].push_back(type); + } + } } // TODO(reference-types): allow the fuzzer to create multiple tables @@ -1129,6 +1141,16 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) { options.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeRefCast); } + if (wasm.features.hasGC()) { + if (typeStructFields.find(type) != typeStructFields.end()) { + options.add(FeatureSet::ReferenceTypes | FeatureSet::GC, + &Self::makeStructGet); + } + if (typeArrays.find(type) != typeArrays.end()) { + options.add(FeatureSet::ReferenceTypes | FeatureSet::GC, + &Self::makeArrayGet); + } + } // TODO: struct.get and other GC things return (this->*pick(options))(type); } @@ -3224,6 +3246,46 @@ Expression* TranslateToFuzzReader::makeRefCast(Type type) { return builder.makeRefCast(make(refType), type, RefCast::Safe); } +Expression* TranslateToFuzzReader::makeStructGet(Type type) { + auto& structFields = typeStructFields[type]; + assert(!structFields.empty()); + auto [structType, fieldIndex] = pick(structFields); + // TODO: also nullable ones? that would increase the risk of traps + auto* ref = make(Type(structType, NonNullable)); + // TODO: fuzz signed and unsigned + return builder.makeStructGet(fieldIndex, ref, type); +} + +Expression* TranslateToFuzzReader::makeArrayGet(Type type) { + auto& arrays = typeArrays[type]; + assert(!arrays.empty()); + auto arrayType = pick(arrays); + // TODO: also nullable ones? that would increase the risk of traps + auto* ref = make(Type(arrayType, NonNullable)); + auto* index = make(Type::i32); + // Only rarely emit a plain get which might trap. See related logic in + // ::makePointer(). + if (allowOOB && oneIn(10)) { + // TODO: fuzz signed and unsigned, and also below + return builder.makeArrayGet(ref, index, type); + } + // To avoid a trap, check the length dynamically using this pattern: + // + // index < array.len ? array[index] : ..some fallback value.. + // + auto tempRef = builder.addVar(funcContext->func, ref->type); + auto tempIndex = builder.addVar(funcContext->func, index->type); + auto* teeRef = builder.makeLocalTee(tempRef, ref, ref->type); + auto* teeIndex = builder.makeLocalTee(tempIndex, index, index->type); + auto* getSize = builder.makeArrayLen(teeRef); + auto* condition = builder.makeBinary(LtUInt32, teeIndex, getSize); + auto* get = builder.makeArrayGet(builder.makeLocalGet(tempRef, ref->type), + builder.makeLocalGet(tempIndex, index->type), + type); + auto* fallback = makeTrivial(type); + return builder.makeIf(condition, get, fallback); +} + Expression* TranslateToFuzzReader::makeI31Get(Type type) { assert(type == Type::i32); assert(wasm.features.hasReferenceTypes() && wasm.features.hasGC()); diff --git a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt index cd33b98f6..71e0b852f 100644 --- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt +++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt @@ -1,40 +1,42 @@ total - [exports] : 4 - [funcs] : 13 + [exports] : 3 + [funcs] : 11 [globals] : 16 [imports] : 5 [memories] : 1 [memory-data] : 20 - [table-data] : 4 + [table-data] : 7 [tables] : 1 [tags] : 2 - [total] : 592 - [vars] : 27 - ArrayNew : 1 - AtomicNotify : 1 - Binary : 71 - Block : 72 - Break : 10 - Call : 20 - Const : 129 + [total] : 593 + [vars] : 14 + ArrayNew : 3 + AtomicCmpxchg : 1 + AtomicFence : 2 + AtomicRMW : 1 + Binary : 72 + Block : 77 + Break : 5 + Call : 14 + CallRef : 1 + Const : 130 Drop : 4 - GlobalGet : 33 - GlobalSet : 33 + GlobalGet : 34 + GlobalSet : 34 I31New : 1 If : 24 - Load : 19 - LocalGet : 44 - LocalSet : 25 - Loop : 4 - Nop : 7 - RefAs : 3 - RefEq : 1 - RefFunc : 10 - RefIsNull : 1 - RefNull : 10 - RefTest : 1 - Return : 4 - Store : 2 + Load : 18 + LocalGet : 34 + LocalSet : 17 + Loop : 7 + Nop : 17 + RefAs : 5 + RefFunc : 13 + RefNull : 12 + Return : 1 + SIMDExtract : 1 + Store : 1 + StructGet : 2 StructNew : 4 TupleExtract : 2 TupleMake : 11 |