diff options
author | Alon Zakai <azakai@google.com> | 2023-04-04 13:38:14 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-04 13:38:14 -0700 |
commit | d7c24bc6796616b821b6b0dfcd649dbf1c821cb3 (patch) | |
tree | 105a556e0853b45669e18a16c45cbfe07df0b150 /src | |
parent | ce2fc9c7cd5158a64631baeda53dac2571038d5f (diff) | |
download | binaryen-d7c24bc6796616b821b6b0dfcd649dbf1c821cb3.tar.gz binaryen-d7c24bc6796616b821b6b0dfcd649dbf1c821cb3.tar.bz2 binaryen-d7c24bc6796616b821b6b0dfcd649dbf1c821cb3.zip |
[Wasm GC] Fuzz struct.new and array.new (#5622)
Repurpose makeBasicRef, makeCompoundRef to generate not just "constant"
refs but any reference, and use those to create StructNew/ArrayNew.
The key changes are to add makeCompoundRef to make(), and to make
the function call make() for children, where possible, instead of just
makeTrivial(). We also replace the i31-specific path with a call to
makeBasicRef which handles i31 among other things.
Diffstat (limited to 'src')
-rw-r--r-- | src/tools/fuzzing.h | 18 | ||||
-rw-r--r-- | src/tools/fuzzing/fuzzing.cpp | 59 |
2 files changed, 48 insertions, 29 deletions
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index ebc77ab43..e53665e14 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -148,11 +148,18 @@ public: struct AutoNester { TranslateToFuzzReader& parent; + size_t amount = 1; AutoNester(TranslateToFuzzReader& parent) : parent(parent) { parent.nesting++; } - ~AutoNester() { parent.nesting--; } + ~AutoNester() { parent.nesting -= amount; } + + // Add more nesting manually. + void add(size_t more) { + parent.nesting += more; + amount += more; + } }; private: @@ -289,10 +296,10 @@ private: // we may add a GC cast to fixup the type. Expression* makeConst(Type type); - // Like makeConst, but for a type that is a reference type. One function - // handles basic types, and the other compound ones. - Expression* makeConstBasicRef(Type type); - Expression* makeConstCompoundRef(Type type); + // Generate reference values. One function handles basic types, and the other + // compound ones. + Expression* makeBasicRef(Type type); + Expression* makeCompoundRef(Type type); Expression* buildUnary(const UnaryArgs& args); Expression* makeUnary(Type type); @@ -319,7 +326,6 @@ private: Expression* makeRefEq(Type type); Expression* makeRefTest(Type type); Expression* makeRefCast(Type type); - Expression* makeI31New(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 4a611345d..d904fd7bd 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -824,6 +824,7 @@ void TranslateToFuzzReader::mutate(Function* func) { // not, changing an offset, etc. // Perform a general replacement. (This is not always valid due to // nesting of labels, but we'll fix that up later.) + // TODO: pick a subtype of the current type replaceCurrent(parent.make(curr->type)); } } @@ -1090,9 +1091,13 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) { options.add(FeatureSet::Multivalue, &Self::makeTupleMake); } if (type.isRef()) { - if (type.getHeapType() == HeapType::i31) { + auto heapType = type.getHeapType(); + if (heapType.isBasic()) { options.add(FeatureSet::ReferenceTypes | FeatureSet::GC, - &Self::makeI31New); + &Self::makeBasicRef); + } else { + options.add(FeatureSet::ReferenceTypes | FeatureSet::GC, + &Self::makeCompoundRef); } options.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeRefCast); @@ -2096,9 +2101,9 @@ Expression* TranslateToFuzzReader::makeConst(Type type) { return builder.makeRefNull(type.getHeapType()); } if (type.getHeapType().isBasic()) { - return makeConstBasicRef(type); + return makeBasicRef(type); } else { - return makeConstCompoundRef(type); + return makeCompoundRef(type); } } else if (type.isTuple()) { std::vector<Expression*> operands; @@ -2112,7 +2117,7 @@ Expression* TranslateToFuzzReader::makeConst(Type type) { } } -Expression* TranslateToFuzzReader::makeConstBasicRef(Type type) { +Expression* TranslateToFuzzReader::makeBasicRef(Type type) { assert(type.isRef()); auto heapType = type.getHeapType(); assert(heapType.isBasic()); @@ -2216,7 +2221,7 @@ Expression* TranslateToFuzzReader::makeConstBasicRef(Type type) { WASM_UNREACHABLE("invalid basic ref type"); } -Expression* TranslateToFuzzReader::makeConstCompoundRef(Type type) { +Expression* TranslateToFuzzReader::makeCompoundRef(Type type) { assert(type.isRef()); auto heapType = type.getHeapType(); assert(!heapType.isBasic()); @@ -2250,28 +2255,43 @@ Expression* TranslateToFuzzReader::makeConstCompoundRef(Type type) { return builder.makeRefAs(RefAsNonNull, builder.makeRefNull(heapType)); } + // When we make children, they must be trivial if we are not in a function + // context. + auto makeChild = [&](Type type) { + return funcContext ? make(type) : makeTrivial(type); + }; + if (heapType.isSignature()) { return makeRefFuncConst(type); } else if (type.isStruct()) { auto& fields = heapType.getStruct().fields; std::vector<Expression*> values; - // TODO: use non-default values randomly even when not necessary, sometimes - if (std::any_of(fields.begin(), fields.end(), [&](const Field& field) { - return !field.type.isDefaultable(); - })) { - // There is a nondefaultable field, which we must create. + // If there is a nondefaultable field, we must provide the value and not + // depend on defaults. Also do that randomly half the time. + if (std::any_of( + fields.begin(), + fields.end(), + [&](const Field& field) { return !field.type.isDefaultable(); }) || + oneIn(2)) { for (auto& field : fields) { - // TODO: when in a function context, we don't need to be trivial. - values.push_back(makeTrivial(field.type)); + values.push_back(makeChild(field.type)); + } + // Add more nesting manually, as we can easily get exponential blowup + // here. This nesting makes it much less likely for a recursive data + // structure to end up as a massive tree of struct.news, since the nesting + // limitation code at the top of this function will kick in. + if (!values.empty()) { + // Subtract 1 since if there is a single value there cannot be + // exponential blowup. + nester.add(values.size() - 1); } } return builder.makeStructNew(heapType, values); } else if (type.isArray()) { auto element = heapType.getArray().element; Expression* init = nullptr; - if (!element.type.isDefaultable()) { - // TODO: when in a function context, we don't need to be trivial. - init = makeTrivial(element.type); + if (!element.type.isDefaultable() || oneIn(2)) { + init = makeChild(element.type); } auto* count = builder.makeConst(int32_t(upTo(MAX_ARRAY_SIZE))); return builder.makeArrayNew(type.getHeapType(), count, init); @@ -3177,13 +3197,6 @@ Expression* TranslateToFuzzReader::makeRefCast(Type type) { return builder.makeRefCast(make(refType), type, RefCast::Safe); } -Expression* TranslateToFuzzReader::makeI31New(Type type) { - assert(type.isRef() && type.getHeapType() == HeapType::i31); - assert(wasm.features.hasReferenceTypes() && wasm.features.hasGC()); - auto* value = make(Type::i32); - return builder.makeI31New(value); -} - Expression* TranslateToFuzzReader::makeI31Get(Type type) { assert(type == Type::i32); assert(wasm.features.hasReferenceTypes() && wasm.features.hasGC()); |