[Fuzzing] Allow recombine() to replace with a subtype (#5101)

Previously it would randomly replace an expression with another one with the
exact same type. Allowing a subtype may give us more coverage.
This commit is contained in:
Alon Zakai 2022-10-03 12:42:03 -07:00 коммит произвёл GitHub
Родитель d9a57f8bac
Коммит fda3cf94fe
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
1 изменённых файлов: 43 добавлений и 4 удалений

Просмотреть файл

@ -623,9 +623,40 @@ void TranslateToFuzzReader::recombine(Function* func) {
void visitExpression(Expression* curr) { void visitExpression(Expression* curr) {
if (parent.canBeArbitrarilyReplaced(curr)) { if (parent.canBeArbitrarilyReplaced(curr)) {
exprsByType[curr->type].push_back(curr); for (auto type : getRelevantTypes(curr->type)) {
exprsByType[type].push_back(curr);
}
} }
} }
std::vector<Type> getRelevantTypes(Type type) {
// Given an expression of a type, we can replace not only other
// expressions with the same type, but also supertypes - since then we'd
// be replacing with a subtype, which is valid.
if (!type.isRef()) {
return {type};
}
std::vector<Type> ret;
auto heapType = type.getHeapType();
auto nullability = type.getNullability();
if (nullability == NonNullable) {
ret = getRelevantTypes(Type(heapType, Nullable));
}
while (1) {
ret.push_back(Type(heapType, nullability));
// TODO: handle basic supertypes too
auto super = heapType.getSuperType();
if (!super) {
break;
}
heapType = *super;
}
return ret;
}
}; };
Scanner scanner(*this); Scanner scanner(*this);
scanner.walk(func->body); scanner.walk(func->body);
@ -653,12 +684,13 @@ void TranslateToFuzzReader::recombine(Function* func) {
} }
} }
// Second, with some probability replace an item with another item having // Second, with some probability replace an item with another item having
// the same type. (This is not always valid due to nesting of labels, but // a proper type. (This is not always valid due to nesting of labels, but
// we'll fix that up later.) // we'll fix that up later.)
struct Modder : public PostWalker<Modder, UnifiedExpressionVisitor<Modder>> { struct Modder : public PostWalker<Modder, UnifiedExpressionVisitor<Modder>> {
Module& wasm; Module& wasm;
Scanner& scanner; Scanner& scanner;
TranslateToFuzzReader& parent; TranslateToFuzzReader& parent;
bool needRefinalize = false;
Modder(Module& wasm, Scanner& scanner, TranslateToFuzzReader& parent) Modder(Module& wasm, Scanner& scanner, TranslateToFuzzReader& parent)
: wasm(wasm), scanner(scanner), parent(parent) {} : wasm(wasm), scanner(scanner), parent(parent) {}
@ -668,13 +700,20 @@ void TranslateToFuzzReader::recombine(Function* func) {
// Replace it! // Replace it!
auto& candidates = scanner.exprsByType[curr->type]; auto& candidates = scanner.exprsByType[curr->type];
assert(!candidates.empty()); // this expression itself must be there assert(!candidates.empty()); // this expression itself must be there
replaceCurrent( auto* rep = parent.pick(candidates);
ExpressionManipulator::copy(parent.pick(candidates), wasm)); replaceCurrent(ExpressionManipulator::copy(rep, wasm));
if (rep->type != curr->type) {
// Subtyping changes require us to finalize later.
needRefinalize = true;
}
} }
} }
}; };
Modder modder(wasm, scanner, *this); Modder modder(wasm, scanner, *this);
modder.walk(func->body); modder.walk(func->body);
if (modder.needRefinalize) {
ReFinalize().walkFunctionInModule(func, &wasm);
}
} }
void TranslateToFuzzReader::mutate(Function* func) { void TranslateToFuzzReader::mutate(Function* func) {