Skip to content

Commit 3f6831c

Browse files
authored
Fuzzer: Fix up rethrows after changes (#7207)
We may move a rethrow into an invalid position when we mutate() etc. This finds such invalid rethrows and replaces them with something trivial so that we validate. To do so, use an expression stack rather than a control flow stack in the fixer, as we need to see which child of a try the rethrow ends up being (so we need the full path from it to the try).
1 parent 3a3cad2 commit 3f6831c

File tree

1 file changed

+44
-4
lines changed

1 file changed

+44
-4
lines changed

src/tools/fuzzing/fuzzing.cpp

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,7 +1568,7 @@ void TranslateToFuzzReader::mutate(Function* func) {
15681568

15691569
void TranslateToFuzzReader::fixAfterChanges(Function* func) {
15701570
struct Fixer
1571-
: public ControlFlowWalker<Fixer, UnifiedExpressionVisitor<Fixer>> {
1571+
: public ExpressionStackWalker<Fixer, UnifiedExpressionVisitor<Fixer>> {
15721572
Module& wasm;
15731573
TranslateToFuzzReader& parent;
15741574

@@ -1606,12 +1606,15 @@ void TranslateToFuzzReader::fixAfterChanges(Function* func) {
16061606
void replace() { replaceCurrent(parent.makeTrivial(getCurrent()->type)); }
16071607

16081608
bool hasBreakTarget(Name name) {
1609-
if (controlFlowStack.empty()) {
1609+
// The break must be on top.
1610+
assert(!expressionStack.empty());
1611+
if (expressionStack.size() < 2) {
1612+
// There must be a scope for this break to be valid.
16101613
return false;
16111614
}
1612-
Index i = controlFlowStack.size() - 1;
1615+
Index i = expressionStack.size() - 2;
16131616
while (1) {
1614-
auto* curr = controlFlowStack[i];
1617+
auto* curr = expressionStack[i];
16151618
bool has = false;
16161619
BranchUtils::operateOnScopeNameDefs(curr, [&](Name& def) {
16171620
if (def == name) {
@@ -1627,6 +1630,43 @@ void TranslateToFuzzReader::fixAfterChanges(Function* func) {
16271630
i--;
16281631
}
16291632
}
1633+
1634+
void visitRethrow(Rethrow* curr) {
1635+
if (!isValidRethrow(curr->target)) {
1636+
replace();
1637+
}
1638+
}
1639+
1640+
bool isValidRethrow(Name target) {
1641+
// The rethrow must be on top.
1642+
assert(!expressionStack.empty());
1643+
assert(expressionStack.back()->is<Rethrow>());
1644+
if (expressionStack.size() < 2) {
1645+
// There must be a try for this rethrow to be valid.
1646+
return false;
1647+
}
1648+
Index i = expressionStack.size() - 2;
1649+
while (1) {
1650+
auto* curr = expressionStack[i];
1651+
if (auto* tryy = curr->dynCast<Try>()) {
1652+
// The rethrow must target a try, and must be nested in a catch of
1653+
// that try (not the body). Look at the child above us to check, when
1654+
// we find the proper try.
1655+
if (tryy->name == target) {
1656+
if (i + 1 >= expressionStack.size()) {
1657+
return false;
1658+
}
1659+
auto* child = expressionStack[i + 1];
1660+
return child != tryy->body;
1661+
}
1662+
}
1663+
if (i == 0) {
1664+
// We never found our try.
1665+
return false;
1666+
}
1667+
i--;
1668+
}
1669+
}
16301670
};
16311671
Fixer fixer(wasm, *this);
16321672
fixer.walk(func->body);

0 commit comments

Comments
 (0)