Skip to content

Commit 33abf08

Browse files
authored
[Wasm GC] Fuzz BrOn (WebAssembly#7006)
1 parent c7e42ae commit 33abf08

File tree

3 files changed

+158
-47
lines changed

3 files changed

+158
-47
lines changed

src/tools/fuzzing.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ class TranslateToFuzzReader {
366366
Expression* makeRefEq(Type type);
367367
Expression* makeRefTest(Type type);
368368
Expression* makeRefCast(Type type);
369+
Expression* makeBrOn(Type type);
369370

370371
// Decide to emit a signed Struct/ArrayGet sometimes, when the field is
371372
// packed.

src/tools/fuzzing/fuzzing.cpp

Lines changed: 118 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,7 +1367,8 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) {
13671367
&Self::makeCallIndirect)
13681368
.add(FeatureSet::ExceptionHandling, &Self::makeTry)
13691369
.add(FeatureSet::ExceptionHandling, &Self::makeTryTable)
1370-
.add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeCallRef);
1370+
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeCallRef)
1371+
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeBrOn);
13711372
}
13721373
if (type.isSingle()) {
13731374
options
@@ -1454,10 +1455,11 @@ Expression* TranslateToFuzzReader::_makenone() {
14541455
.add(FeatureSet::Atomics, &Self::makeAtomic)
14551456
.add(FeatureSet::ExceptionHandling, &Self::makeTry)
14561457
.add(FeatureSet::ExceptionHandling, &Self::makeTryTable)
1457-
.add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeCallRef)
1458-
.add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeStructSet)
1459-
.add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeArraySet)
1460-
.add(FeatureSet::GC | FeatureSet::ReferenceTypes,
1458+
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeCallRef)
1459+
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeStructSet)
1460+
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeArraySet)
1461+
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeBrOn)
1462+
.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
14611463
&Self::makeArrayBulkMemoryOp);
14621464
return (this->*pick(options))(Type::none);
14631465
}
@@ -1484,7 +1486,7 @@ Expression* TranslateToFuzzReader::_makeunreachable() {
14841486
&Self::makeDrop,
14851487
&Self::makeReturn)
14861488
.add(FeatureSet::ExceptionHandling, &Self::makeThrow)
1487-
.add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeCallRef);
1489+
.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeCallRef);
14881490
return (this->*pick(options))(Type::unreachable);
14891491
}
14901492

@@ -3944,6 +3946,116 @@ Expression* TranslateToFuzzReader::makeRefCast(Type type) {
39443946
return builder.makeRefCast(make(refType), type);
39453947
}
39463948

3949+
Expression* TranslateToFuzzReader::makeBrOn(Type type) {
3950+
if (funcContext->breakableStack.empty()) {
3951+
return makeTrivial(type);
3952+
}
3953+
// We need to find a proper target to break to; try a few times. Finding the
3954+
// target is harder than flowing out the proper type, so focus on the target,
3955+
// and fix up the flowing type later. That is, once we find a target to break
3956+
// to, we can then either drop ourselves or wrap ourselves in a block +
3957+
// another value, so that we return the proper thing here (which is done below
3958+
// in fixFlowingType).
3959+
int tries = TRIES;
3960+
Name targetName;
3961+
Type targetType;
3962+
while (--tries >= 0) {
3963+
auto* target = pick(funcContext->breakableStack);
3964+
targetName = getTargetName(target);
3965+
targetType = getTargetType(target);
3966+
// We can send any reference type, or no value at all, but nothing else.
3967+
if (targetType.isRef() || targetType == Type::none) {
3968+
break;
3969+
}
3970+
}
3971+
if (tries < 0) {
3972+
return makeTrivial(type);
3973+
}
3974+
3975+
auto fixFlowingType = [&](Expression* brOn) -> Expression* {
3976+
if (Type::isSubType(brOn->type, type)) {
3977+
// Already of the proper type.
3978+
return brOn;
3979+
}
3980+
if (type == Type::none) {
3981+
// We just need to drop whatever it is.
3982+
return builder.makeDrop(brOn);
3983+
}
3984+
// We need to replace the type with something else. Drop the BrOn if we need
3985+
// to, and append a value with the proper type.
3986+
if (brOn->type != Type::none) {
3987+
brOn = builder.makeDrop(brOn);
3988+
}
3989+
return builder.makeSequence(brOn, make(type));
3990+
};
3991+
3992+
// We found something to break to. Figure out which BrOn variants we can
3993+
// send.
3994+
if (targetType == Type::none) {
3995+
// BrOnNull is the only variant that sends no value.
3996+
return fixFlowingType(
3997+
builder.makeBrOn(BrOnNull, targetName, make(getReferenceType())));
3998+
}
3999+
4000+
// We are sending a reference type to the target. All other BrOn variants can
4001+
// do that.
4002+
assert(targetType.isRef());
4003+
auto op = pick(BrOnNonNull, BrOnCast, BrOnCastFail);
4004+
Type castType = Type::none;
4005+
Type refType;
4006+
switch (op) {
4007+
case BrOnNonNull: {
4008+
// The sent type is the non-nullable version of the reference, so any ref
4009+
// of that type is ok, nullable or not.
4010+
refType = Type(targetType.getHeapType(), getNullability());
4011+
break;
4012+
}
4013+
case BrOnCast: {
4014+
// The sent type is the heap type we cast to, with the input type's
4015+
// nullability, so the combination of the two must be a subtype of
4016+
// targetType.
4017+
castType = getSubType(targetType);
4018+
// The ref's type must be castable to castType, or we'd not validate. But
4019+
// it can also be a subtype, which will trivially also succeed (so do that
4020+
// more rarely). Pick subtypes rarely, as they make the cast trivial.
4021+
refType = oneIn(5) ? getSubType(castType) : getSuperType(castType);
4022+
if (targetType.isNonNullable()) {
4023+
// And it must have the right nullability for the target, as mentioned
4024+
// above: if the target type is non-nullable then either the ref or the
4025+
// cast types must be.
4026+
if (!refType.isNonNullable() && !castType.isNonNullable()) {
4027+
// Pick one to make non-nullable.
4028+
if (oneIn(2)) {
4029+
refType = Type(refType.getHeapType(), NonNullable);
4030+
} else {
4031+
castType = Type(castType.getHeapType(), NonNullable);
4032+
}
4033+
}
4034+
}
4035+
break;
4036+
}
4037+
case BrOnCastFail: {
4038+
// The sent type is the ref's type, with adjusted nullability (if the cast
4039+
// allows nulls then no null can fail the cast, and what is sent is non-
4040+
// nullable). First, pick a ref type that we can send to the target.
4041+
refType = getSubType(targetType);
4042+
// See above on BrOnCast, but flipped.
4043+
castType = oneIn(5) ? getSuperType(refType) : getSubType(refType);
4044+
// There is no nullability to adjust: if targetType is non-nullable then
4045+
// both refType and castType are as well, as subtypes of it. But we can
4046+
// also allow castType to be nullable (it is not sent to the target).
4047+
if (castType.isNonNullable() && oneIn(2)) {
4048+
castType = Type(castType.getHeapType(), Nullable);
4049+
}
4050+
} break;
4051+
default: {
4052+
WASM_UNREACHABLE("bad br_on op");
4053+
}
4054+
}
4055+
return fixFlowingType(
4056+
builder.makeBrOn(op, targetName, make(refType), castType));
4057+
}
4058+
39474059
bool TranslateToFuzzReader::maybeSignedGet(const Field& field) {
39484060
if (field.isPacked()) {
39494061
return oneIn(2);
Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,51 @@
11
Metrics
22
total
3-
[exports] : 3
4-
[funcs] : 5
3+
[exports] : 4
4+
[funcs] : 7
55
[globals] : 26
66
[imports] : 5
77
[memories] : 1
88
[memory-data] : 20
9-
[table-data] : 0
9+
[table-data] : 2
1010
[tables] : 1
1111
[tags] : 2
12-
[total] : 499
13-
[vars] : 20
12+
[total] : 510
13+
[vars] : 15
1414
ArrayNew : 14
1515
ArrayNewFixed : 2
16-
AtomicCmpxchg : 1
17-
AtomicNotify : 1
18-
AtomicRMW : 1
19-
Binary : 69
20-
Block : 42
21-
Break : 8
22-
Call : 6
23-
Const : 126
24-
Drop : 2
25-
GlobalGet : 27
16+
AtomicFence : 1
17+
Binary : 64
18+
Block : 45
19+
Break : 2
20+
Call : 8
21+
CallRef : 2
22+
Const : 127
23+
Drop : 5
24+
GlobalGet : 28
2625
GlobalSet : 16
27-
I31Get : 1
28-
If : 10
26+
If : 12
2927
Load : 18
30-
LocalGet : 43
31-
LocalSet : 22
32-
Loop : 5
33-
Nop : 3
34-
Pop : 3
35-
RefAs : 2
36-
RefFunc : 2
37-
RefI31 : 1
38-
RefNull : 8
39-
RefTest : 1
40-
Return : 1
41-
Select : 1
42-
Store : 3
43-
StringConst : 9
44-
StringEncode : 1
45-
StringEq : 3
46-
StructNew : 12
47-
StructSet : 1
48-
Try : 3
49-
TryTable : 2
50-
TupleExtract : 1
51-
TupleMake : 4
52-
Unary : 13
53-
Unreachable : 11
28+
LocalGet : 46
29+
LocalSet : 29
30+
Loop : 3
31+
MemoryCopy : 1
32+
MemoryInit : 1
33+
Nop : 4
34+
Pop : 2
35+
RefCast : 1
36+
RefFunc : 7
37+
RefNull : 6
38+
Return : 9
39+
SIMDExtract : 1
40+
Store : 1
41+
StringConst : 7
42+
StringEq : 1
43+
StringMeasure : 1
44+
StringSliceWTF : 1
45+
StructNew : 14
46+
Try : 2
47+
TryTable : 1
48+
TupleExtract : 3
49+
TupleMake : 6
50+
Unary : 9
51+
Unreachable : 10

0 commit comments

Comments
 (0)