diff options
Diffstat (limited to 'src/tools/fuzzing.h')
-rw-r--r-- | src/tools/fuzzing.h | 278 |
1 files changed, 170 insertions, 108 deletions
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 24e1d6e8e..c1a81a896 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -228,6 +228,12 @@ private: // The maximum amount of vars in each function. static const int MAX_VARS = 20; + // The maximum number of globals in a module. + static const int MAX_GLOBALS = 20; + + // The maximum number of tuple elements. + static const int MAX_TUPLE_SIZE = 6; + // some things require luck, try them a few times static const int TRIES = 10; @@ -309,22 +315,29 @@ private: double getDouble() { return Literal(get64()).reinterpretf64(); } - SmallVector<Type, 2> getSubTypes(Type type) { - SmallVector<Type, 2> ret; - ret.push_back(type); // includes itself + Type getSubType(Type type) { + if (type.isMulti()) { + std::vector<Type> types; + for (auto t : type.expand()) { + types.push_back(getSubType(t)); + } + return Type(types); + } + SmallVector<Type, 2> options; + options.push_back(type); // includes itself switch (type.getSingle()) { case Type::anyref: - ret.push_back(Type::funcref); - ret.push_back(Type::exnref); + options.push_back(Type::funcref); + options.push_back(Type::exnref); // falls through case Type::funcref: case Type::exnref: - ret.push_back(Type::nullref); + options.push_back(Type::nullref); break; default: break; } - return ret; + return pick(options); } void setupMemory() { @@ -406,34 +419,25 @@ private: std::map<Type, std::vector<Name>> globalsByType; void setupGlobals() { - size_t index = 0; - for (auto type : getConcreteTypes()) { - auto num = upTo(3); - for (size_t i = 0; i < num; i++) { - auto* glob = - builder.makeGlobal(std::string("global$") + std::to_string(index++), - type, - makeConst(type), - Builder::Mutable); - wasm.addGlobal(glob); - globalsByType[type].push_back(glob->name); - } + for (size_t index = upTo(MAX_GLOBALS); index > 0; --index) { + auto type = getConcreteType(); + auto* global = + builder.makeGlobal(std::string("global$") + std::to_string(index), + type, + makeConst(type), + Builder::Mutable); + wasm.addGlobal(global); + globalsByType[type].push_back(global->name); } } void setupEvents() { Index num = upTo(3); for (size_t i = 0; i < num; i++) { - // Events should have void return type and at least one param type - std::vector<Type> params; - Index numValues = - wasm.features.hasMultivalue() ? upToSquared(MAX_PARAMS) : upTo(2); - for (Index i = 0; i < numValues; i++) { - params.push_back(getConcreteType()); - } - auto* event = builder.makeEvent(std::string("event$") + std::to_string(i), - WASM_EVENT_ATTRIBUTE_EXCEPTION, - Signature(Type(params), Type::none)); + auto* event = + builder.makeEvent(std::string("event$") + std::to_string(i), + WASM_EVENT_ATTRIBUTE_EXCEPTION, + Signature(getControlFlowType(), Type::none)); wasm.addEvent(event); } } @@ -544,11 +548,11 @@ private: std::vector<Type> params; params.reserve(numParams); for (Index i = 0; i < numParams; i++) { - auto type = getConcreteType(); + auto type = getSingleConcreteType(); typeLocals[type].push_back(params.size()); params.push_back(type); } - func->sig = Signature(Type(params), getReachableType()); + func->sig = Signature(Type(params), getControlFlowType()); Index numVars = upToSquared(MAX_VARS); for (Index i = 0; i < numVars; i++) { auto type = getConcreteType(); @@ -859,32 +863,22 @@ private: } nesting++; Expression* ret = nullptr; - switch (type.getSingle()) { - case Type::i32: - case Type::i64: - case Type::f32: - case Type::f64: - case Type::v128: - case Type::funcref: - case Type::anyref: - case Type::nullref: - case Type::exnref: - ret = _makeConcrete(type); - break; - case Type::none: - ret = _makenone(); - break; - case Type::unreachable: - ret = _makeunreachable(); - break; + if (type.isConcrete()) { + ret = _makeConcrete(type); + } else if (type == Type::none) { + ret = _makenone(); + } else { + assert(type == Type::unreachable); + ret = _makeunreachable(); } // we should create the right type of thing assert(Type::isSubType(ret->type, type)); nesting--; return ret; } - Expression* _makeConcrete(Type type) { + bool canMakeControlFlow = + !type.isMulti() || wasm.features.has(FeatureSet::Multivalue); auto choice = upTo(100); if (choice < 10) { return makeConst(type); @@ -895,42 +889,55 @@ private: if (choice < 50) { return makeLocalGet(type); } - if (choice < 60) { + if (choice < 60 && canMakeControlFlow) { return makeBlock(type); } - if (choice < 70) { + if (choice < 70 && canMakeControlFlow) { return makeIf(type); } - if (choice < 80) { + if (choice < 80 && canMakeControlFlow) { return makeLoop(type); } - if (choice < 90) { + if (choice < 90 && canMakeControlFlow) { return makeBreak(type); } using Self = TranslateToFuzzReader; - auto options = FeatureOptions<Expression* (Self::*)(Type)>() - .add(FeatureSet::MVP, - &Self::makeBlock, - &Self::makeIf, - &Self::makeLoop, - &Self::makeBreak, - &Self::makeCall, - &Self::makeCallIndirect, - &Self::makeLocalGet, - &Self::makeLocalSet, - &Self::makeLoad, - &Self::makeConst, - &Self::makeUnary, - &Self::makeBinary, - &Self::makeSelect, - &Self::makeGlobalGet) - .add(FeatureSet::SIMD, &Self::makeSIMD); - if (type == Type::i32 || type == Type::i64) { + FeatureOptions<Expression* (Self::*)(Type)> options; + options.add(FeatureSet::MVP, + &Self::makeLocalGet, + &Self::makeLocalSet, + &Self::makeGlobalGet, + &Self::makeConst); + if (canMakeControlFlow) { + options.add(FeatureSet::MVP, + &Self::makeBlock, + &Self::makeIf, + &Self::makeLoop, + &Self::makeBreak, + &Self::makeCall, + &Self::makeCallIndirect); + } + if (type.isSingle()) { + options + .add(FeatureSet::MVP, + &Self::makeUnary, + &Self::makeBinary, + &Self::makeSelect) + .add(FeatureSet::Multivalue, &Self::makeTupleExtract); + } + if (type.isSingle() && !type.isRef()) { + options.add(FeatureSet::MVP, &Self::makeLoad); + options.add(FeatureSet::SIMD, &Self::makeSIMD); + } + if (type.isInteger()) { options.add(FeatureSet::Atomics, &Self::makeAtomic); } if (type == Type::i32) { options.add(FeatureSet::ReferenceTypes, &Self::makeRefIsNull); } + if (type.isMulti()) { + options.add(FeatureSet::Multivalue, &Self::makeTupleMake); + } return (this->*pick(options))(type); } @@ -1314,22 +1321,60 @@ private: } Expression* makeGlobalGet(Type type) { - auto& globals = globalsByType[type]; - if (globals.empty()) { + auto it = globalsByType.find(type); + if (it == globalsByType.end() || it->second.empty()) { return makeConst(type); } - return builder.makeGlobalGet(pick(globals), type); + return builder.makeGlobalGet(pick(it->second), type); } Expression* makeGlobalSet(Type type) { assert(type == Type::none); type = getConcreteType(); - auto& globals = globalsByType[type]; - if (globals.empty()) { + auto it = globalsByType.find(type); + if (it == globalsByType.end() || it->second.empty()) { return makeTrivial(Type::none); } auto* value = make(type); - return builder.makeGlobalSet(pick(globals), value); + return builder.makeGlobalSet(pick(it->second), value); + } + + Expression* makeTupleMake(Type type) { + assert(wasm.features.hasMultivalue()); + assert(type.isMulti()); + std::vector<Expression*> elements; + for (auto t : type.expand()) { + elements.push_back(make(t)); + } + return builder.makeTupleMake(std::move(elements)); + } + + Expression* makeTupleExtract(Type type) { + assert(wasm.features.hasMultivalue()); + assert(type.isSingle() && type.isConcrete()); + Type tupleType = getTupleType(); + auto& elements = tupleType.expand(); + + // Find indices from which we can extract `type` + std::vector<size_t> extractIndices; + for (size_t i = 0; i < elements.size(); ++i) { + if (elements[i] == type) { + extractIndices.push_back(i); + } + } + + // If there are none, inject one + if (extractIndices.size() == 0) { + auto newElements = elements; + size_t injected = upTo(elements.size()); + newElements[injected] = type; + tupleType = Type(newElements); + extractIndices.push_back(injected); + } + + Index index = pick(extractIndices); + Expression* child = make(tupleType); + return builder.makeTupleExtract(child, index); } Expression* makePointer() { @@ -1782,6 +1827,13 @@ private: } return builder.makeRefNull(); } + if (type.isMulti()) { + std::vector<Expression*> operands; + for (auto t : type.expand()) { + operands.push_back(makeConst(t)); + } + return builder.makeTupleMake(std::move(operands)); + } auto* ret = wasm.allocator.alloc<Const>(); ret->value = makeLiteral(type); ret->type = type; @@ -1793,8 +1845,9 @@ private: } Expression* makeUnary(Type type) { + assert(!type.isMulti()); if (type == Type::unreachable) { - if (auto* unary = makeUnary(getConcreteType())->dynCast<Unary>()) { + if (auto* unary = makeUnary(getSingleConcreteType())->dynCast<Unary>()) { return makeDeNanOp( builder.makeUnary(unary->op, make(Type::unreachable))); } @@ -1808,7 +1861,7 @@ private: switch (type.getSingle()) { case Type::i32: { - switch (getConcreteType().getSingle()) { + switch (getSingleConcreteType().getSingle()) { case Type::i32: { auto op = pick( FeatureOptions<UnaryOp>() @@ -2012,8 +2065,10 @@ private: } Expression* makeBinary(Type type) { + assert(!type.isMulti()); if (type == Type::unreachable) { - if (auto* binary = makeBinary(getConcreteType())->dynCast<Binary>()) { + if (auto* binary = + makeBinary(getSingleConcreteType())->dynCast<Binary>()) { return makeDeNanOp(buildBinary( {binary->op, make(Type::unreachable), make(Type::unreachable)})); } @@ -2247,8 +2302,8 @@ private: } Expression* makeSelect(Type type) { - Type subType1 = pick(getSubTypes(type)); - Type subType2 = pick(getSubTypes(type)); + Type subType1 = getSubType(type); + Type subType2 = getSubType(type); return makeDeNanOp( buildSelect({make(Type::i32), make(subType1), make(subType2)}, type)); } @@ -2677,16 +2732,10 @@ private: } // special getters - - std::vector<Type> getReachableTypes() { + std::vector<Type> getSingleConcreteTypes() { return items( FeatureOptions<Type>() - .add(FeatureSet::MVP, - Type::i32, - Type::i64, - Type::f32, - Type::f64, - Type::none) + .add(FeatureSet::MVP, Type::i32, Type::i64, Type::f32, Type::f64) .add(FeatureSet::SIMD, Type::v128) .add(FeatureSet::ReferenceTypes, Type::funcref, @@ -2695,34 +2744,46 @@ private: .add(FeatureSet::ReferenceTypes | FeatureSet::ExceptionHandling, Type::exnref)); } - Type getReachableType() { return pick(getReachableTypes()); } - std::vector<Type> getConcreteTypes() { - return items( - FeatureOptions<Type>() - .add(FeatureSet::MVP, Type::i32, Type::i64, Type::f32, Type::f64) - .add(FeatureSet::SIMD, Type::v128) - .add(FeatureSet::ReferenceTypes, - Type::funcref, - Type::anyref, - Type::nullref) - .add(FeatureSet::ReferenceTypes | FeatureSet::ExceptionHandling, - Type::exnref)); + Type getSingleConcreteType() { return pick(getSingleConcreteTypes()); } + + Type getTupleType() { + std::vector<Type> elements; + size_t numElements = 2 + upTo(MAX_TUPLE_SIZE - 1); + elements.resize(numElements); + for (size_t i = 0; i < numElements; ++i) { + elements[i] = getSingleConcreteType(); + } + return Type(elements); } - Type getConcreteType() { return pick(getConcreteTypes()); } - // Get types that can be stored in memory - std::vector<Type> getStorableTypes() { - return items( + Type getConcreteType() { + if (wasm.features.hasMultivalue() && oneIn(5)) { + return getTupleType(); + } else { + return getSingleConcreteType(); + } + } + + Type getControlFlowType() { + if (oneIn(10)) { + return Type::none; + } else { + return getConcreteType(); + } + } + + Type getStorableType() { + return pick( FeatureOptions<Type>() .add(FeatureSet::MVP, Type::i32, Type::i64, Type::f32, Type::f64) .add(FeatureSet::SIMD, Type::v128)); } - Type getStorableType() { return pick(getStorableTypes()); } // - funcref cannot be logged because referenced functions can be inlined or // removed during optimization // - there's no point in logging anyref because it is opaque + // - don't bother logging tuples std::vector<Type> getLoggableTypes() { return items( FeatureOptions<Type>() @@ -2732,6 +2793,7 @@ private: .add(FeatureSet::ReferenceTypes | FeatureSet::ExceptionHandling, Type::exnref)); } + Type getLoggableType() { return pick(getLoggableTypes()); } // statistical distributions |