summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2023-04-17 16:17:23 -0700
committerGitHub <noreply@github.com>2023-04-17 16:17:23 -0700
commita8507782d9eeaa30061b0036a506e155cfde8f20 (patch)
tree11595baf4e7aea71f4b8875c11c7408b3e9b062e
parent5679e3b1ea0d4155a6b5c3b93fa6aef34023365f (diff)
downloadbinaryen-a8507782d9eeaa30061b0036a506e155cfde8f20.tar.gz
binaryen-a8507782d9eeaa30061b0036a506e155cfde8f20.tar.bz2
binaryen-a8507782d9eeaa30061b0036a506e155cfde8f20.zip
[Wasm GC] Fuzz array.copy and array.fill (#5663)
-rw-r--r--src/tools/fuzzing.h4
-rw-r--r--src/tools/fuzzing/fuzzing.cpp88
-rw-r--r--test/passes/translate-to-fuzz_all-features_metrics_noprint.txt65
3 files changed, 126 insertions, 31 deletions
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index abc0a4248..5a9ebdf22 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -353,6 +353,10 @@ private:
Expression* makeStructSet(Type type);
Expression* makeArrayGet(Type type);
Expression* makeArraySet(Type type);
+ // Use a single method for the misc array operations, to not give them too
+ // much representation (e.g. compared to struct operations, which only include
+ // get/set).
+ Expression* makeArrayBulkMemoryOp(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 c88b2f13d..74d209493 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -1196,7 +1196,9 @@ Expression* TranslateToFuzzReader::_makenone() {
.add(FeatureSet::Atomics, &Self::makeAtomic)
.add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeCallRef)
.add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeStructSet)
- .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeArraySet);
+ .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeArraySet)
+ .add(FeatureSet::GC | FeatureSet::ReferenceTypes,
+ &Self::makeArrayBulkMemoryOp);
return (this->*pick(options))(Type::none);
}
@@ -3332,16 +3334,33 @@ Expression* TranslateToFuzzReader::makeStructSet(Type type) {
return builder.makeStructSet(fieldIndex, ref, value);
}
+// Make a bounds check for an array operation, given a ref + index. An optional
+// additional length parameter can be provided, which is added to the index if
+// so (that is useful for something like array.fill, which operations on not a
+// single item like array.set, but a range).
static auto makeArrayBoundsCheck(Expression* ref,
Expression* index,
Function* func,
- Builder& builder) {
+ Builder& builder,
+ Expression* length = nullptr) {
auto tempRef = builder.addVar(func, ref->type);
auto tempIndex = builder.addVar(func, index->type);
auto* teeRef = builder.makeLocalTee(tempRef, ref, ref->type);
auto* teeIndex = builder.makeLocalTee(tempIndex, index, index->type);
auto* getSize = builder.makeArrayLen(teeRef);
+ Expression* effectiveIndex = teeIndex;
+
+ Expression* getLength = nullptr;
+ if (length) {
+ // Store the length so we can reuse it.
+ auto tempLength = builder.addVar(func, length->type);
+ auto* teeLength = builder.makeLocalTee(tempLength, length, length->type);
+ // The effective index will now include the length.
+ effectiveIndex = builder.makeBinary(AddInt32, effectiveIndex, teeLength);
+ getLength = builder.makeLocalGet(tempLength, length->type);
+ }
+
struct BoundsCheck {
// A condition that checks if the index is in bounds.
Expression* condition;
@@ -3350,9 +3369,12 @@ static auto makeArrayBoundsCheck(Expression* ref,
Expression* getRef;
// An addition use of the index (as with the ref, it reads from a local).
Expression* getIndex;
- } result = {builder.makeBinary(LtUInt32, teeIndex, getSize),
+ // An addition use of the length, if it was provided.
+ Expression* getLength = nullptr;
+ } result = {builder.makeBinary(LtUInt32, effectiveIndex, getSize),
builder.makeLocalGet(tempRef, ref->type),
- builder.makeLocalGet(tempIndex, index->type)};
+ builder.makeLocalGet(tempIndex, index->type),
+ getLength};
return result;
}
@@ -3403,6 +3425,64 @@ Expression* TranslateToFuzzReader::makeArraySet(Type type) {
return builder.makeIf(check.condition, set);
}
+Expression* TranslateToFuzzReader::makeArrayBulkMemoryOp(Type type) {
+ assert(type == Type::none);
+ if (mutableArrays.empty()) {
+ return makeTrivial(type);
+ }
+ auto arrayType = pick(mutableArrays);
+ auto element = arrayType.getArray().element;
+ auto* index = make(Type::i32);
+ auto* ref = makeTrappingRefUse(arrayType);
+ if (oneIn(2)) {
+ // ArrayFill
+ auto* value = make(element.type);
+ auto* length = 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.makeArrayFill(ref, index, value, length);
+ }
+ auto check =
+ makeArrayBoundsCheck(ref, index, funcContext->func, builder, length);
+ auto* fill = builder.makeArrayFill(
+ check.getRef, check.getIndex, value, check.getLength);
+ return builder.makeIf(check.condition, fill);
+ } else {
+ // ArrayCopy. Here we must pick a source array whose element type is a
+ // subtype of the destination.
+ auto srcArrayType = pick(mutableArrays);
+ auto srcElement = srcArrayType.getArray().element;
+ if (!Type::isSubType(srcElement.type, element.type) ||
+ element.packedType != srcElement.packedType) {
+ // TODO: A matrix of which arrays are subtypes of others. For now, if we
+ // didn't get what we want randomly, just copy from the same type to
+ // itself.
+ srcArrayType = arrayType;
+ srcElement = element;
+ }
+ auto* srcIndex = make(Type::i32);
+ auto* srcRef = makeTrappingRefUse(srcArrayType);
+ auto* length = make(Type::i32);
+ if (allowOOB && oneIn(10)) {
+ // TODO: fuzz signed and unsigned, and also below
+ return builder.makeArrayCopy(ref, index, srcRef, srcIndex, length);
+ }
+ auto check =
+ makeArrayBoundsCheck(ref, index, funcContext->func, builder, length);
+ auto srcCheck = makeArrayBoundsCheck(
+ srcRef, srcIndex, funcContext->func, builder, check.getLength);
+ auto* copy = builder.makeArrayCopy(check.getRef,
+ check.getIndex,
+ srcCheck.getRef,
+ srcCheck.getIndex,
+ srcCheck.getLength);
+ return builder.makeIf(check.condition,
+ builder.makeIf(srcCheck.condition, copy));
+ }
+}
+
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 e9a93af72..16eb0cbd8 100644
--- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
+++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
@@ -1,36 +1,47 @@
total
- [exports] : 2
- [funcs] : 7
+ [exports] : 6
+ [funcs] : 15
[globals] : 16
[imports] : 5
[memories] : 1
[memory-data] : 20
- [table-data] : 1
+ [table-data] : 3
[tables] : 1
[tags] : 2
- [total] : 350
- [vars] : 10
- ArrayLen : 1
- ArrayNew : 2
- ArraySet : 1
- Binary : 61
- Block : 27
- Const : 87
- GlobalGet : 16
- GlobalSet : 16
+ [total] : 725
+ [vars] : 22
+ ArrayFill : 1
+ ArrayLen : 3
+ ArrayNew : 7
+ ArraySet : 2
+ AtomicCmpxchg : 1
+ AtomicFence : 1
+ AtomicNotify : 3
+ Binary : 85
+ Block : 90
+ Break : 14
+ Call : 14
+ CallIndirect : 1
+ Const : 173
+ Drop : 4
+ GlobalGet : 40
+ GlobalSet : 40
I31New : 1
- If : 11
- Load : 18
- LocalGet : 40
- LocalSet : 21
- Loop : 2
- Nop : 1
- RefFunc : 6
- RefIsNull : 1
- RefNull : 4
+ If : 29
+ Load : 17
+ LocalGet : 42
+ LocalSet : 28
+ Loop : 6
+ Nop : 13
+ RefAs : 7
+ RefFunc : 9
+ RefNull : 12
Return : 4
- Store : 1
- StructNew : 2
- TupleMake : 7
- Unary : 9
- Unreachable : 11
+ SIMDExtract : 1
+ Store : 5
+ StructGet : 2
+ StructNew : 7
+ TupleExtract : 2
+ TupleMake : 10
+ Unary : 28
+ Unreachable : 23