summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/tools/fuzzing.h8
-rw-r--r--src/tools/fuzzing/fuzzing.cpp66
-rw-r--r--test/passes/translate-to-fuzz_all-features_metrics_noprint.txt67
3 files changed, 106 insertions, 35 deletions
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 24f08dc53..f055d7f44 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -124,6 +124,12 @@ private:
// Type => list of array types that have that type.
std::unordered_map<Type, std::vector<HeapType>> typeArrays;
+ // All struct fields that are mutable.
+ std::vector<StructField> mutableStructFields;
+
+ // All arrays that are mutable.
+ std::vector<HeapType> mutableArrays;
+
Index numAddedFunctions = 0;
// RAII helper for managing the state used to create a single function.
@@ -334,7 +340,9 @@ private:
Expression* makeRefTest(Type type);
Expression* makeRefCast(Type type);
Expression* makeStructGet(Type type);
+ Expression* makeStructSet(Type type);
Expression* makeArrayGet(Type type);
+ Expression* makeArraySet(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 fd8722224..1c2b818df 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -287,10 +287,22 @@ void TranslateToFuzzReader::setupHeapTypes() {
interestingHeapSubTypes[HeapType::struct_].push_back(type);
interestingHeapSubTypes[HeapType::eq].push_back(type);
interestingHeapSubTypes[HeapType::any].push_back(type);
+
+ // Note the mutable fields.
+ auto& fields = type.getStruct().fields;
+ for (Index i = 0; i < fields.size(); i++) {
+ if (fields[i].mutable_) {
+ mutableStructFields.push_back(StructField{type, i});
+ }
+ }
} else if (type.isArray()) {
interestingHeapSubTypes[HeapType::array].push_back(type);
interestingHeapSubTypes[HeapType::eq].push_back(type);
interestingHeapSubTypes[HeapType::any].push_back(type);
+
+ if (type.getArray().element.mutable_) {
+ mutableArrays.push_back(type);
+ }
} else if (type.isSignature()) {
interestingHeapSubTypes[HeapType::func].push_back(type);
}
@@ -1182,7 +1194,9 @@ Expression* TranslateToFuzzReader::_makenone() {
&Self::makeGlobalSet)
.add(FeatureSet::BulkMemory, &Self::makeBulkMemory)
.add(FeatureSet::Atomics, &Self::makeAtomic)
- .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeCallRef);
+ .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeCallRef)
+ .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeStructSet)
+ .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeArraySet);
return (this->*pick(options))(Type::none);
}
@@ -3251,11 +3265,28 @@ Expression* TranslateToFuzzReader::makeStructGet(Type type) {
assert(!structFields.empty());
auto [structType, fieldIndex] = pick(structFields);
// TODO: also nullable ones? that would increase the risk of traps
+ // TODO: Ensure a good chance to use a local.get or tee here, as we want to
+ // test the same reference having multiple sets/gets on it, and not
+ // gets/sets of struct.news everywhere. Also in struct.set, array.get,
+ // array.set.
auto* ref = make(Type(structType, NonNullable));
// TODO: fuzz signed and unsigned
return builder.makeStructGet(fieldIndex, ref, type);
}
+Expression* TranslateToFuzzReader::makeStructSet(Type type) {
+ assert(type == Type::none);
+ if (mutableStructFields.empty()) {
+ return makeTrivial(type);
+ }
+ auto [structType, fieldIndex] = pick(mutableStructFields);
+ auto fieldType = structType.getStruct().fields[fieldIndex].type;
+ // TODO: also nullable ones? that would increase the risk of traps
+ auto* ref = make(Type(structType, NonNullable));
+ auto* value = make(fieldType);
+ return builder.makeStructSet(fieldIndex, ref, value);
+}
+
Expression* TranslateToFuzzReader::makeArrayGet(Type type) {
auto& arrays = typeArrays[type];
assert(!arrays.empty());
@@ -3286,6 +3317,39 @@ Expression* TranslateToFuzzReader::makeArrayGet(Type type) {
return builder.makeIf(condition, get, fallback);
}
+Expression* TranslateToFuzzReader::makeArraySet(Type type) {
+ assert(type == Type::none);
+ if (mutableArrays.empty()) {
+ return makeTrivial(type);
+ }
+ auto arrayType = pick(mutableArrays);
+ auto elementType = arrayType.getArray().element.type;
+ auto* index = make(Type::i32);
+ // TODO: also nullable ones? that would increase the risk of traps
+ auto* ref = make(Type(arrayType, NonNullable));
+ auto* value = make(elementType);
+ // 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.makeArraySet(ref, index, value);
+ }
+ // To avoid a trap, check the length dynamically using this pattern:
+ //
+ // if (index < array.len) array[index] = 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* refGet = builder.makeLocalGet(tempRef, ref->type);
+ auto* indexGet = builder.makeLocalGet(tempIndex, index->type);
+ auto* set = builder.makeArraySet(refGet, indexGet, value);
+ return builder.makeIf(condition, set);
+}
+
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 71e0b852f..6d2aba469 100644
--- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
+++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
@@ -1,44 +1,43 @@
total
- [exports] : 3
+ [exports] : 4
[funcs] : 11
[globals] : 16
[imports] : 5
[memories] : 1
[memory-data] : 20
- [table-data] : 7
+ [table-data] : 2
[tables] : 1
[tags] : 2
- [total] : 593
- [vars] : 14
- ArrayNew : 3
- AtomicCmpxchg : 1
- AtomicFence : 2
- AtomicRMW : 1
- Binary : 72
- Block : 77
- Break : 5
- Call : 14
- CallRef : 1
- Const : 130
+ [total] : 521
+ [vars] : 21
+ ArrayLen : 1
+ ArrayNew : 6
+ ArrayNewFixed : 1
+ ArraySet : 1
+ Binary : 66
+ Block : 58
+ Break : 3
+ Call : 7
+ Const : 117
Drop : 4
- GlobalGet : 34
- GlobalSet : 34
- I31New : 1
- If : 24
- Load : 18
- LocalGet : 34
- LocalSet : 17
- Loop : 7
- Nop : 17
- RefAs : 5
- RefFunc : 13
- RefNull : 12
+ GlobalGet : 32
+ GlobalSet : 32
+ I31New : 2
+ If : 19
+ Load : 17
+ LocalGet : 41
+ LocalSet : 23
+ Loop : 6
+ Nop : 3
+ RefAs : 4
+ RefEq : 1
+ RefFunc : 10
+ RefNull : 8
Return : 1
- SIMDExtract : 1
- Store : 1
- StructGet : 2
- StructNew : 4
- TupleExtract : 2
- TupleMake : 11
- Unary : 25
- Unreachable : 20
+ Select : 1
+ StructNew : 5
+ StructSet : 1
+ TupleExtract : 1
+ TupleMake : 9
+ Unary : 22
+ Unreachable : 19