Skip to content

Commit 737417d

Browse files
authored
[Stack Switching] Improve cont.bind fuzzing (#8500)
* Pick a signature from the known interesting types. * Pick a continuation type using that signature.
1 parent 58de22c commit 737417d

2 files changed

Lines changed: 91 additions & 14 deletions

File tree

src/tools/fuzzing.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ class TranslateToFuzzReader {
217217
// All arrays that are mutable.
218218
std::vector<HeapType> mutableArrays;
219219

220+
// Mapping of signatures to the continuations they are used by.
221+
std::unordered_map<HeapType, std::vector<HeapType>> sigConts;
222+
220223
// All tags that are valid as exception tags (which cannot have results).
221224
std::vector<Tag*> exceptionTags;
222225

src/tools/fuzzing/fuzzing.cpp

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ void TranslateToFuzzReader::setupHeapTypes() {
574574
break;
575575
case HeapTypeKind::Cont:
576576
interestingHeapSubTypes[cont].push_back(type);
577+
sigConts[type.getContinuation().type].push_back(type);
577578
break;
578579
case HeapTypeKind::Basic:
579580
WASM_UNREACHABLE("unexpected kind");
@@ -5458,20 +5459,93 @@ Expression* TranslateToFuzzReader::makeBrOn(Type type) {
54585459
}
54595460

54605461
Expression* TranslateToFuzzReader::makeContBind(Type type) {
5461-
auto sig = type.getHeapType().getContinuation().type.getSignature();
5462-
// Add a single param to be bound. TODO: Add multiple, and look in
5463-
// interestingHeapTypes.
5464-
std::vector<Type> newParams;
5465-
for (auto t : sig.params) {
5466-
newParams.push_back(t);
5467-
}
5468-
auto newParam = getSingleConcreteType();
5469-
newParams.insert(newParams.begin(), newParam);
5470-
auto newSig = Signature(Type(newParams), sig.results);
5471-
auto newCont = Continuation(newSig);
5472-
auto newType = Type(newCont, NonNullable, Exact);
5473-
std::vector<Expression*> newArgs{make(newParam)};
5474-
return builder.makeContBind(type.getHeapType(), newArgs, make(newType));
5462+
// We must output a signature that corresponds to the type we were given.
5463+
auto outputSigType = type.getHeapType().getContinuation().type;
5464+
auto outputSig = outputSigType.getSignature();
5465+
auto numOutputParams = outputSig.params.size();
5466+
5467+
// Look for a compatible signature. Don't always do this, however - we have a
5468+
// few other options below.
5469+
std::optional<HeapType> inputSigType;
5470+
if (!oneIn(4)) {
5471+
auto& funcTypes = interestingHeapSubTypes[HeapTypes::func];
5472+
// Filter out incompatible signatures.
5473+
std::vector<HeapType> relevantFuncTypes;
5474+
for (auto funcType : funcTypes) {
5475+
auto funcSig = funcType.getSignature();
5476+
if (funcSig.results != outputSig.results) {
5477+
// The results must match.
5478+
continue;
5479+
}
5480+
5481+
// The params must be compatible. For example, with output params [x,y,z]
5482+
// we'd want an input signature like [a,b,x,y,z] so that we can bind a and
5483+
// b.
5484+
auto numInputParams = funcSig.params.size();
5485+
if (numInputParams < numOutputParams) {
5486+
// Too short.
5487+
continue;
5488+
}
5489+
// Ignoring the input params at the start, compare the tails.
5490+
auto numAddedParams = numInputParams - numOutputParams;
5491+
bool bad = false;
5492+
for (Index i = 0; i < numOutputParams; i++) {
5493+
if (!Type::isSubType(outputSig.params[i],
5494+
funcSig.params[numAddedParams + i])) {
5495+
bad = true;
5496+
break;
5497+
}
5498+
}
5499+
if (!bad) {
5500+
relevantFuncTypes.push_back(funcType);
5501+
}
5502+
}
5503+
if (!relevantFuncTypes.empty()) {
5504+
inputSigType = pick(relevantFuncTypes);
5505+
}
5506+
}
5507+
5508+
Index numAddedParams;
5509+
if (inputSigType) {
5510+
// We picked a signature, above.
5511+
numAddedParams =
5512+
inputSigType->getSignature().params.size() - numOutputParams;
5513+
} else {
5514+
// We failed to find a signature, either use the current one (binding no
5515+
// input params) or invent a input one, adding one param.
5516+
if (oneIn(2)) {
5517+
inputSigType = outputSigType;
5518+
numAddedParams = 0;
5519+
} else {
5520+
std::vector<Type> inputParams;
5521+
for (auto t : outputSig.params) {
5522+
inputParams.push_back(t);
5523+
}
5524+
auto inputParam = getSingleConcreteType();
5525+
inputParams.insert(inputParams.begin(), inputParam);
5526+
inputSigType = Signature(Type(inputParams), outputSig.results);
5527+
numAddedParams = 1;
5528+
}
5529+
}
5530+
auto inputSig = inputSigType->getSignature();
5531+
5532+
// Pick a continuation type for the signature. If existing continuations use
5533+
// it, usually pick one of them.
5534+
auto& inputSigConts = sigConts[*inputSigType];
5535+
HeapType inputCont;
5536+
if (!inputSigConts.empty() && !oneIn(5)) {
5537+
inputCont = pick(inputSigConts);
5538+
} else {
5539+
inputCont = Continuation(inputSig);
5540+
}
5541+
Type inputType = Type(inputCont, NonNullable, Exact);
5542+
5543+
// Generate the new args and the cont.bind.
5544+
std::vector<Expression*> newArgs;
5545+
for (Index i = 0; i < numAddedParams; i++) {
5546+
newArgs.push_back(make(inputSig.params[i]));
5547+
}
5548+
return builder.makeContBind(type.getHeapType(), newArgs, make(inputType));
54755549
}
54765550

54775551
bool TranslateToFuzzReader::maybeSignedGet(const Field& field) {

0 commit comments

Comments
 (0)