Skip to content

Commit 96e8bcb

Browse files
Fuzzing support for waitqueue instructions
1 parent 0624377 commit 96e8bcb

5 files changed

Lines changed: 191 additions & 102 deletions

File tree

‎src/tools/fuzzing.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ class TranslateToFuzzReader {
220220
// All struct fields that are mutable.
221221
std::vector<StructField> mutableStructFields;
222222

223+
// All struct fields that can be waited on.
224+
std::vector<StructField> structWaitFields;
225+
223226
// All arrays that are mutable.
224227
std::vector<HeapType> mutableArrays;
225228

@@ -560,6 +563,8 @@ class TranslateToFuzzReader {
560563
Expression* makeStructRMW(Type type);
561564
Expression* makeStructCmpxchg(Type type);
562565
Expression* makeStructSet(Type type);
566+
Expression* makeStructWait(Type type);
567+
Expression* makeWaitqueueNotify(Type type);
563568
Expression* makeArrayGet(Type type);
564569
Expression* makeArraySet(Type type);
565570
Expression* makeArrayRMW(Type type);

‎src/tools/fuzzing/fuzzing.cpp‎

Lines changed: 85 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -603,11 +603,19 @@ void TranslateToFuzzReader::setupHeapTypes() {
603603
interestingHeapSubTypes[struct_].push_back(type);
604604
interestingHeapSubTypes[eq].push_back(type);
605605
interestingHeapSubTypes[any].push_back(type);
606-
// Note the mutable fields.
607-
auto& fields = type.getStruct().fields;
606+
// Note the mutable fields and fields that can be waited on.
607+
const auto& fields = type.getStruct().fields;
608608
for (Index i = 0; i < fields.size(); i++) {
609609
if (fields[i].mutable_) {
610-
mutableStructFields.push_back(StructField{type, i});
610+
mutableStructFields.emplace_back(type, i);
611+
}
612+
if (!fields[i].isPacked()) {
613+
auto fieldType = fields[i].type;
614+
if (fieldType == Type::i32 || fieldType == Type::i64 ||
615+
Type::isSubType(
616+
fieldType, Type(HeapTypes::eq.getBasic(Shared), Nullable))) {
617+
structWaitFields.emplace_back(type, i);
618+
}
611619
}
612620
}
613621
break;
@@ -1752,6 +1760,18 @@ void TranslateToFuzzReader::processFunctions() {
17521760
}
17531761
}
17541762

1763+
// if (!ATOMIC_WAITS) {
1764+
// for (auto& func : wasm.functions) {
1765+
// if (!func->imported()) {
1766+
// for (auto* wait : FindAll<StructWait>(func->body).list) {
1767+
// if (wait->timeout->type == Type::i64) {
1768+
// wait->timeout = builder.makeConst(int64_t(0));
1769+
// }
1770+
// }
1771+
// }
1772+
// }
1773+
// }
1774+
17551775
// Also fix up closed world, if we need to. We must do this at the end, so
17561776
// nothing can break the closed world assumptions after.
17571777
if (worldMode == WorldMode::Closed) {
@@ -1901,6 +1921,13 @@ void TranslateToFuzzReader::addHangLimitChecks(Function* func) {
19011921
AndInt32, arrayNew->size, builder.makeConst(int32_t(1024 - 1)));
19021922
}
19031923
}
1924+
if (!ATOMIC_WAITS) {
1925+
for (auto* wait : FindAll<StructWait>(func->body).list) {
1926+
if (wait->timeout->type == Type::i64) {
1927+
wait->timeout = builder.makeConst(int64_t(0));
1928+
}
1929+
}
1930+
}
19041931
}
19051932

19061933
void TranslateToFuzzReader::recombine(Function* func) {
@@ -2436,6 +2463,14 @@ void TranslateToFuzzReader::fixAfterChanges(Function* func) {
24362463
} fixer(wasm, *this);
24372464
fixer.walk(func->body);
24382465

2466+
// if (!ATOMIC_WAITS) {
2467+
// for (auto* wait : FindAll<StructWait>(func->body).list) {
2468+
// if (wait->timeout->type == Type::i64) {
2469+
// wait->timeout = builder.makeConst(int64_t(0));
2470+
// }
2471+
// }
2472+
// }
2473+
24392474
// Refinalize at the end, after labels are all fixed up.
24402475
ReFinalize().walkFunctionInModule(func, &wasm);
24412476
}
@@ -2881,6 +2916,13 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) {
28812916
&Self::makeStringEq,
28822917
&Self::makeStringMeasure,
28832918
&Self::makeStringGet);
2919+
options.add(FeatureSet::ReferenceTypes | FeatureSet::SharedEverything,
2920+
&Self::makeWaitqueueNotify);
2921+
if (!structWaitFields.empty()) {
2922+
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC |
2923+
FeatureSet::SharedEverything,
2924+
&Self::makeStructWait);
2925+
}
28842926
}
28852927
if (type == Type::i64) {
28862928
options.add(FeatureSet::WideArithmetic | FeatureSet::Multivalue,
@@ -4407,17 +4449,20 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
44074449
case HeapType::noext:
44084450
case HeapType::nofunc:
44094451
case HeapType::nocont:
4410-
case HeapType::noexn: {
4452+
case HeapType::noexn:
4453+
case HeapType::nowaitqueue: {
44114454
auto null = builder.makeRefNull(heapType.getBasic(share));
44124455
if (!type.isNullable()) {
44134456
return builder.makeRefAs(RefAsNonNull, null);
44144457
}
44154458
return null;
44164459
}
44174460

4418-
case HeapType::waitqueue:
4419-
case HeapType::nowaitqueue: {
4420-
WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer");
4461+
case HeapType::waitqueue: {
4462+
if (type.isNullable() && oneIn(2)) {
4463+
return builder.makeRefNull(HeapTypes::sharedWaitqueue.getBasic(share));
4464+
}
4465+
return builder.makeWaitqueueNew();
44214466
}
44224467
}
44234468
WASM_UNREACHABLE("invalid basic ref type");
@@ -6060,8 +6105,8 @@ Expression* TranslateToFuzzReader::makeStructSet(Type type) {
60606105
return makeTrivial(type);
60616106
}
60626107
auto [structType, fieldIndex] = pick(mutableStructFields);
6063-
auto fieldType = structType.getStruct().fields[fieldIndex].type;
60646108
auto* ref = makeTrappingRefUse(structType);
6109+
auto fieldType = structType.getStruct().fields[fieldIndex].type;
60656110
auto* value = make(fieldType);
60666111
auto order = MemoryOrder::Unordered;
60676112
if (wasm.features.hasAtomics() && wasm.features.hasSharedEverything() &&
@@ -6071,6 +6116,35 @@ Expression* TranslateToFuzzReader::makeStructSet(Type type) {
60716116
return builder.makeStructSet(fieldIndex, ref, value, order);
60726117
}
60736118

6119+
Expression* TranslateToFuzzReader::makeStructWait(Type type) {
6120+
assert(type == Type::i32);
6121+
assert(!structWaitFields.empty());
6122+
auto [structType, fieldIndex] = pick(structWaitFields);
6123+
auto* ref = makeTrappingRefUse(structType);
6124+
auto* waitqueue =
6125+
makeTrappingRefUse(Type(HeapTypes::sharedWaitqueue, Nullable));
6126+
auto expectedType = structType.getStruct().fields[fieldIndex].type;
6127+
if (expectedType.isRef()) {
6128+
expectedType = Type(HeapTypes::eq.getBasic(Shared), Nullable);
6129+
}
6130+
auto* expected = make(expectedType);
6131+
Expression* timeout = nullptr;
6132+
if (ATOMIC_WAITS && oneIn(2)) {
6133+
timeout = make(Type::i64);
6134+
} else {
6135+
timeout = builder.makeConst(int64_t{0});
6136+
}
6137+
return builder.makeStructWait(fieldIndex, ref, waitqueue, expected, timeout);
6138+
}
6139+
6140+
Expression* TranslateToFuzzReader::makeWaitqueueNotify(Type type) {
6141+
assert(type == Type::i32);
6142+
auto* waitqueue =
6143+
makeTrappingRefUse(Type(HeapTypes::sharedWaitqueue, Nullable));
6144+
auto* count = make(Type::i32);
6145+
return builder.makeWaitqueueNotify(waitqueue, count);
6146+
}
6147+
60746148
// Make a bounds check for an array operation, given a ref + index. An optional
60756149
// additional length parameter can be provided, which is added to the index if
60766150
// so (that is useful for something like array.fill, which operations on not a
@@ -6705,11 +6779,11 @@ HeapType TranslateToFuzzReader::getSubType(HeapType type) {
67056779
case HeapType::nofunc:
67066780
case HeapType::nocont:
67076781
case HeapType::noexn:
6782+
case HeapType::nowaitqueue:
67086783
break;
67096784
case HeapType::waitqueue:
6710-
case HeapType::nowaitqueue: {
6711-
WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer");
6712-
}
6785+
return pick(HeapTypes::sharedWaitqueue, HeapTypes::sharedNowaitqueue)
6786+
.getBasic(share);
67136787
}
67146788
}
67156789
// Look for an interesting subtype.

‎src/tools/fuzzing/heap-types.cpp‎

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,9 @@ struct HeapTypeGeneratorImpl {
344344
if (features.hasStackSwitching() && share == Unshared) {
345345
bottoms.push_back(HeapType::nocont);
346346
}
347+
if (features.hasSharedEverything() && share == Shared) {
348+
bottoms.push_back(HeapType::nowaitqueue);
349+
}
347350
return rand.pick(bottoms).getBasic(share);
348351
}
349352

@@ -366,6 +369,9 @@ struct HeapTypeGeneratorImpl {
366369
if (features.hasExceptionHandling() && share == Unshared) {
367370
options.push_back(HeapType::exn);
368371
}
372+
if (features.hasSharedEverything() && share == Shared) {
373+
options.push_back(HeapType::waitqueue);
374+
}
369375
auto ht = rand.pick(options);
370376
return ht.getBasic(share);
371377
}
@@ -691,11 +697,13 @@ struct HeapTypeGeneratorImpl {
691697
case HeapType::nofunc:
692698
case HeapType::nocont:
693699
case HeapType::noexn:
700+
case HeapType::nowaitqueue:
694701
return type;
695702
case HeapType::waitqueue:
696-
case HeapType::nowaitqueue: {
697-
WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer");
698-
}
703+
if (rand.oneIn(2)) {
704+
return HeapTypes::sharedNowaitqueue.getBasic(share);
705+
}
706+
return type;
699707
}
700708
WASM_UNREACHABLE("unexpected type");
701709
}
@@ -743,6 +751,7 @@ struct HeapTypeGeneratorImpl {
743751
case HeapType::exn:
744752
case HeapType::cont:
745753
case HeapType::any:
754+
case HeapType::waitqueue:
746755
break;
747756
case HeapType::eq:
748757
candidates.push_back(HeapTypes::any.getBasic(share));
@@ -768,10 +777,9 @@ struct HeapTypeGeneratorImpl {
768777
case HeapType::noexn:
769778
candidates.push_back(HeapTypes::exn.getBasic(share));
770779
break;
771-
case HeapType::waitqueue:
772-
case HeapType::nowaitqueue: {
773-
WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer");
774-
}
780+
case HeapType::nowaitqueue:
781+
candidates.push_back(HeapTypes::sharedWaitqueue.getBasic(share));
782+
break;
775783
}
776784
assert(!candidates.empty());
777785
return rand.pick(candidates);

‎test/lit/fuzz-types.test‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
;; RUN: wasm-fuzz-types -v --seed=3 | filecheck %s
22

3-
;; CHECK: Running with seed 3
3+
;; CHECK: Running with seed 3
44
;; CHECK-NEXT: Built 20 types:
55
;; CHECK-NEXT: (rec
66
;; CHECK-NEXT: (type $0 (sub (shared (func (param i64 f64 exnref (ref null $0)) (result (ref cont))))))

0 commit comments

Comments
 (0)