Skip to content

Commit a046452

Browse files
Fuzzing support for waitqueue instructions
1 parent 91a6772 commit a046452

5 files changed

Lines changed: 157 additions & 56 deletions

File tree

src/ir/subtype-exprs.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,22 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
383383
self()->noteSubtype(curr->expected, expectedType);
384384
self()->noteSubtype(curr->replacement, type);
385385
}
386-
void visitStructWait(StructWait* curr) {}
386+
void visitStructWait(StructWait* curr) {
387+
self()->noteSubtype(curr->waitqueue,
388+
Type(HeapTypes::sharedWaitqueue, Nullable));
389+
if (!curr->ref->type.isStruct()) {
390+
return;
391+
}
392+
const auto& fields = curr->ref->type.getHeapType().getStruct().fields;
393+
if (curr->index < fields.size()) {
394+
self()->noteSubtype(curr->expected, fields[curr->index].type);
395+
}
396+
}
387397
void visitWaitqueueNew(WaitqueueNew* curr) {}
388-
void visitWaitqueueNotify(WaitqueueNotify* curr) {}
398+
void visitWaitqueueNotify(WaitqueueNotify* curr) {
399+
self()->noteSubtype(curr->waitqueue,
400+
Type(HeapTypes::sharedWaitqueue, Nullable));
401+
}
389402
void visitArrayNew(ArrayNew* curr) {
390403
if (!curr->type.isArray() || curr->isWithDefault()) {
391404
return;

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

@@ -559,6 +562,8 @@ class TranslateToFuzzReader {
559562
Expression* makeStructRMW(Type type);
560563
Expression* makeStructCmpxchg(Type type);
561564
Expression* makeStructSet(Type type);
565+
Expression* makeStructWait(Type type);
566+
Expression* makeWaitqueueNotify(Type type);
562567
Expression* makeArrayGet(Type type);
563568
Expression* makeArraySet(Type type);
564569
Expression* makeArrayRMW(Type type);

src/tools/fuzzing/fuzzing.cpp

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -560,12 +560,20 @@ void TranslateToFuzzReader::setupHeapTypes() {
560560
interestingHeapSubTypes[struct_].push_back(type);
561561
interestingHeapSubTypes[eq].push_back(type);
562562
interestingHeapSubTypes[any].push_back(type);
563-
// Note the mutable fields.
563+
// Note the mutable fields and fields that can be waited on.
564564
auto& fields = type.getStruct().fields;
565565
for (Index i = 0; i < fields.size(); i++) {
566566
if (fields[i].mutable_) {
567567
mutableStructFields.push_back(StructField{type, i});
568568
}
569+
if (!fields[i].isPacked()) {
570+
auto fieldType = fields[i].type;
571+
if (fieldType == Type::i32 || fieldType == Type::i64 ||
572+
Type::isSubType(
573+
fieldType, Type(HeapTypes::eq.getBasic(Shared), Nullable))) {
574+
structWaitFields.push_back(StructField{type, i});
575+
}
576+
}
569577
}
570578
break;
571579
}
@@ -1709,6 +1717,18 @@ void TranslateToFuzzReader::processFunctions() {
17091717
}
17101718
}
17111719

1720+
if (!ATOMIC_WAITS) {
1721+
for (auto& func : wasm.functions) {
1722+
if (!func->imported()) {
1723+
for (auto* wait : FindAll<StructWait>(func->body).list) {
1724+
if (wait->timeout->type == Type::i64) {
1725+
wait->timeout = builder.makeConst(int64_t(0));
1726+
}
1727+
}
1728+
}
1729+
}
1730+
}
1731+
17121732
// Also fix up closed world, if we need to. We must do this at the end, so
17131733
// nothing can break the closed world assumptions after.
17141734
if (worldMode == WorldMode::Closed) {
@@ -1858,6 +1878,13 @@ void TranslateToFuzzReader::addHangLimitChecks(Function* func) {
18581878
AndInt32, arrayNew->size, builder.makeConst(int32_t(1024 - 1)));
18591879
}
18601880
}
1881+
if (!ATOMIC_WAITS) {
1882+
for (auto* wait : FindAll<StructWait>(func->body).list) {
1883+
if (wait->timeout->type == Type::i64) {
1884+
wait->timeout = builder.makeConst(int64_t(0));
1885+
}
1886+
}
1887+
}
18611888
}
18621889

18631890
void TranslateToFuzzReader::recombine(Function* func) {
@@ -2393,6 +2420,14 @@ void TranslateToFuzzReader::fixAfterChanges(Function* func) {
23932420
} fixer(wasm, *this);
23942421
fixer.walk(func->body);
23952422

2423+
if (!ATOMIC_WAITS) {
2424+
for (auto* wait : FindAll<StructWait>(func->body).list) {
2425+
if (wait->timeout->type == Type::i64) {
2426+
wait->timeout = builder.makeConst(int64_t(0));
2427+
}
2428+
}
2429+
}
2430+
23962431
// Refinalize at the end, after labels are all fixed up.
23972432
ReFinalize().walkFunctionInModule(func, &wasm);
23982433
}
@@ -2838,6 +2873,11 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) {
28382873
&Self::makeStringEq,
28392874
&Self::makeStringMeasure,
28402875
&Self::makeStringGet);
2876+
options.add(FeatureSet::ReferenceTypes | FeatureSet::SharedEverything,
2877+
&Self::makeWaitqueueNotify);
2878+
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC |
2879+
FeatureSet::SharedEverything,
2880+
&Self::makeStructWait);
28412881
}
28422882
if (type.isTuple()) {
28432883
if (type == Types::getI64Pair() && oneIn(2)) {
@@ -4349,17 +4389,20 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
43494389
case HeapType::noext:
43504390
case HeapType::nofunc:
43514391
case HeapType::nocont:
4352-
case HeapType::noexn: {
4392+
case HeapType::noexn:
4393+
case HeapType::nowaitqueue: {
43534394
auto null = builder.makeRefNull(heapType.getBasic(share));
43544395
if (!type.isNullable()) {
43554396
return builder.makeRefAs(RefAsNonNull, null);
43564397
}
43574398
return null;
43584399
}
43594400

4360-
case HeapType::waitqueue:
4361-
case HeapType::nowaitqueue: {
4362-
WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer");
4401+
case HeapType::waitqueue: {
4402+
if (type.isNullable() && oneIn(2)) {
4403+
return builder.makeRefNull(HeapTypes::sharedWaitqueue.getBasic(share));
4404+
}
4405+
return builder.makeWaitqueueNew();
43634406
}
43644407
}
43654408
WASM_UNREACHABLE("invalid basic ref type");
@@ -6002,8 +6045,11 @@ Expression* TranslateToFuzzReader::makeStructSet(Type type) {
60026045
return makeTrivial(type);
60036046
}
60046047
auto [structType, fieldIndex] = pick(mutableStructFields);
6005-
auto fieldType = structType.getStruct().fields[fieldIndex].type;
60066048
auto* ref = makeTrappingRefUse(structType);
6049+
auto fieldType = structType.getStruct().fields[fieldIndex].type;
6050+
if (ref->type.isStruct()) {
6051+
fieldType = ref->type.getHeapType().getStruct().fields[fieldIndex].type;
6052+
}
60076053
auto* value = make(fieldType);
60086054
auto order = MemoryOrder::Unordered;
60096055
if (wasm.features.hasAtomics() && wasm.features.hasSharedEverything() &&
@@ -6013,6 +6059,35 @@ Expression* TranslateToFuzzReader::makeStructSet(Type type) {
60136059
return builder.makeStructSet(fieldIndex, ref, value, order);
60146060
}
60156061

6062+
Expression* TranslateToFuzzReader::makeStructWait(Type type) {
6063+
assert(type == Type::i32);
6064+
if (structWaitFields.empty()) {
6065+
return makeTrivial(type);
6066+
}
6067+
auto [structType, fieldIndex] = pick(structWaitFields);
6068+
auto* ref = makeTrappingRefUse(structType);
6069+
auto* waitqueue = make(Type(HeapTypes::sharedWaitqueue, Nullable));
6070+
auto fieldType = structType.getStruct().fields[fieldIndex].type;
6071+
if (ref->type.isStruct()) {
6072+
fieldType = ref->type.getHeapType().getStruct().fields[fieldIndex].type;
6073+
}
6074+
auto* expected = make(fieldType);
6075+
Expression* timeout = nullptr;
6076+
if (ATOMIC_WAITS && oneIn(2)) {
6077+
timeout = make(Type::i64);
6078+
} else {
6079+
timeout = builder.makeConst(int64_t(0));
6080+
}
6081+
return builder.makeStructWait(fieldIndex, ref, waitqueue, expected, timeout);
6082+
}
6083+
6084+
Expression* TranslateToFuzzReader::makeWaitqueueNotify(Type type) {
6085+
assert(type == Type::i32);
6086+
auto* waitqueue = make(Type(HeapTypes::sharedWaitqueue, Nullable));
6087+
auto* count = make(Type::i32);
6088+
return builder.makeWaitqueueNotify(waitqueue, count);
6089+
}
6090+
60166091
// Make a bounds check for an array operation, given a ref + index. An optional
60176092
// additional length parameter can be provided, which is added to the index if
60186093
// so (that is useful for something like array.fill, which operations on not a
@@ -6647,11 +6722,11 @@ HeapType TranslateToFuzzReader::getSubType(HeapType type) {
66476722
case HeapType::nofunc:
66486723
case HeapType::nocont:
66496724
case HeapType::noexn:
6725+
case HeapType::nowaitqueue:
66506726
break;
66516727
case HeapType::waitqueue:
6652-
case HeapType::nowaitqueue: {
6653-
WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer");
6654-
}
6728+
return pick(HeapTypes::sharedWaitqueue, HeapTypes::sharedNowaitqueue)
6729+
.getBasic(share);
66556730
}
66566731
}
66576732
// 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
@@ -343,6 +343,9 @@ struct HeapTypeGeneratorImpl {
343343
if (features.hasStackSwitching() && share == Unshared) {
344344
bottoms.push_back(HeapType::nocont);
345345
}
346+
if (features.hasSharedEverything() && share == Shared) {
347+
bottoms.push_back(HeapType::nowaitqueue);
348+
}
346349
return rand.pick(bottoms).getBasic(share);
347350
}
348351

@@ -365,6 +368,9 @@ struct HeapTypeGeneratorImpl {
365368
if (features.hasExceptionHandling() && share == Unshared) {
366369
options.push_back(HeapType::exn);
367370
}
371+
if (features.hasSharedEverything() && share == Shared) {
372+
options.push_back(HeapType::waitqueue);
373+
}
368374
auto ht = rand.pick(options);
369375
return ht.getBasic(share);
370376
}
@@ -690,11 +696,13 @@ struct HeapTypeGeneratorImpl {
690696
case HeapType::nofunc:
691697
case HeapType::nocont:
692698
case HeapType::noexn:
699+
case HeapType::nowaitqueue:
693700
return type;
694701
case HeapType::waitqueue:
695-
case HeapType::nowaitqueue: {
696-
WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer");
697-
}
702+
if (rand.oneIn(2)) {
703+
return HeapTypes::sharedNowaitqueue.getBasic(share);
704+
}
705+
return type;
698706
}
699707
WASM_UNREACHABLE("unexpected type");
700708
}
@@ -742,6 +750,7 @@ struct HeapTypeGeneratorImpl {
742750
case HeapType::exn:
743751
case HeapType::cont:
744752
case HeapType::any:
753+
case HeapType::waitqueue:
745754
break;
746755
case HeapType::eq:
747756
candidates.push_back(HeapTypes::any.getBasic(share));
@@ -767,10 +776,9 @@ struct HeapTypeGeneratorImpl {
767776
case HeapType::noexn:
768777
candidates.push_back(HeapTypes::exn.getBasic(share));
769778
break;
770-
case HeapType::waitqueue:
771-
case HeapType::nowaitqueue: {
772-
WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer");
773-
}
779+
case HeapType::nowaitqueue:
780+
candidates.push_back(HeapTypes::sharedWaitqueue.getBasic(share));
781+
break;
774782
}
775783
assert(!candidates.empty());
776784
return rand.pick(candidates);

test/lit/fuzz-types.test

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,61 +4,61 @@
44
;; CHECK-NEXT: Built 20 types:
55
;; CHECK-NEXT: (rec
66
;; CHECK-NEXT: (type $0 (sub (shared (func (result (ref $3) i64)))))
7-
;; CHECK-NEXT: (type $1 (sub (shared (struct (field (mut (ref null (shared extern)))) (field (ref null $3)) (field (mut v128)) (field v128) (field (mut (ref (shared any)))) (field (ref (shared i31)))))))
8-
;; CHECK-NEXT: (type $2 (sub (func (param (ref $1)))))
9-
;; CHECK-NEXT: (type $3 (shared (struct (field f32) (field (mut (ref null (shared eq)))) (field (ref (shared extern))) (field (mut (ref null (shared struct)))) (field (mut f32)) (field (mut (ref null $0))))))
10-
;; CHECK-NEXT: (type $4 (sub (func (result i64))))
7+
;; CHECK-NEXT: (type $1 (sub (shared (struct (field (mut (ref (shared extern)))) (field (ref null $0)) (field (mut v128)) (field v128) (field (mut (ref (shared eq)))) (field (ref $1))))))
8+
;; CHECK-NEXT: (type $2 (sub (func (result i64))))
9+
;; CHECK-NEXT: (type $3 (shared (struct (field i64) (field (ref null (shared i31))) (field i16) (field (mut i8)) (field (mut (ref null (shared any)))))))
10+
;; CHECK-NEXT: (type $4 (sub (func (param (ref $3) eqref) (result (ref null $4)))))
1111
;; CHECK-NEXT: )
1212
;; CHECK-NEXT: (rec
13-
;; CHECK-NEXT: (type $5 (struct (field (mut (ref $0))) (field i8) (field i31ref) (field (ref func)) (field f32)))
14-
;; CHECK-NEXT: (type $6 (sub (func (param f64 (ref $7) (ref null $5) f32) (result i32))))
15-
;; CHECK-NEXT: (type $7 (sub (shared (func (param (ref null $4) (ref null $6))))))
13+
;; CHECK-NEXT: (type $5 (struct (field (mut v128))))
14+
;; CHECK-NEXT: (type $6 (sub (func (param f64 (ref $1)) (result v128))))
15+
;; CHECK-NEXT: (type $7 (sub (shared (func (param (ref null $4) (ref $0) (ref $2) i32 v128) (result (ref null $1))))))
1616
;; CHECK-NEXT: )
1717
;; CHECK-NEXT: (rec
18-
;; CHECK-NEXT: (type $8 (struct (field (mut v128)) (field (mut (ref $5))) (field (mut v128)) (field (mut f64)) (field (mut i8))))
19-
;; CHECK-NEXT: (type $9 (shared (struct (field i32) (field (mut i32)) (field (mut i16)))))
20-
;; CHECK-NEXT: (type $10 (sub (func (param f32 (ref null $4) f64) (result (ref (shared struct))))))
18+
;; CHECK-NEXT: (type $8 (struct (field (mut (ref $5))) (field v128)))
19+
;; CHECK-NEXT: (type $9 (sub (shared (struct (field i64)))))
20+
;; CHECK-NEXT: (type $10 (sub (func (result v128))))
2121
;; CHECK-NEXT: )
2222
;; CHECK-NEXT: (rec
23-
;; CHECK-NEXT: (type $11 (cont $16))
24-
;; CHECK-NEXT: (type $12 (sub final $10 (func (param f32 funcref f64) (result (ref $9)))))
25-
;; CHECK-NEXT: (type $13 (cont $19))
26-
;; CHECK-NEXT: (type $14 (sub $2 (func (param (ref null (shared struct))))))
27-
;; CHECK-NEXT: (type $15 (sub (struct (field (mut (ref $7))) (field (mut i8)) (field (mut v128)) (field f64) (field f64) (field i64))))
28-
;; CHECK-NEXT: (type $16 (sub final $14 (func (param (ref null (shared eq))))))
29-
;; CHECK-NEXT: (type $17 (sub (func (param f32 v128 (ref $13)) (result (ref $5)))))
30-
;; CHECK-NEXT: (type $18 (struct (field (ref null $6)) (field (ref $17)) (field (mut f64)) (field (mut i64))))
31-
;; CHECK-NEXT: (type $19 (sub $4 (func (result i64))))
23+
;; CHECK-NEXT: (type $11 (sub (cont $6)))
24+
;; CHECK-NEXT: (type $12 (sub $10 (func (result v128))))
25+
;; CHECK-NEXT: (type $13 (cont $10))
26+
;; CHECK-NEXT: (type $14 (sub $2 (func (result i64))))
27+
;; CHECK-NEXT: (type $15 (sub (struct (field (mut (ref $3))) (field (mut (ref null $19))) (field i64) (field (ref $13)) (field i8) (field (mut i16)))))
28+
;; CHECK-NEXT: (type $16 (sub $14 (func (result i64))))
29+
;; CHECK-NEXT: (type $17 (sub (func (result f64))))
30+
;; CHECK-NEXT: (type $18 (struct (field f32) (field (mut (ref $6))) (field (mut f64)) (field f32)))
31+
;; CHECK-NEXT: (type $19 (sub $4 (func (param (ref (shared any)) anyref) (result (ref null $19)))))
3232
;; CHECK-NEXT: )
3333
;; CHECK-NEXT:
3434
;; CHECK-NEXT: Inhabitable types:
3535
;; CHECK-NEXT:
3636
;; CHECK-NEXT: Built 20 types:
3737
;; CHECK-NEXT: (rec
3838
;; CHECK-NEXT: (type $0 (sub (shared (func (result (ref $3) i64)))))
39-
;; CHECK-NEXT: (type $1 (sub (shared (struct (field (mut (ref null (shared extern)))) (field (ref null $3)) (field (mut v128)) (field v128) (field (mut (ref (shared any)))) (field (ref (shared i31)))))))
40-
;; CHECK-NEXT: (type $2 (sub (func (param (ref $1)))))
41-
;; CHECK-NEXT: (type $3 (shared (struct (field f32) (field (mut (ref null (shared eq)))) (field (ref null (shared extern))) (field (mut (ref null (shared struct)))) (field (mut f32)) (field (mut (ref null $0))))))
42-
;; CHECK-NEXT: (type $4 (sub (func (result i64))))
39+
;; CHECK-NEXT: (type $1 (sub (shared (struct (field (mut (ref null (shared extern)))) (field (ref null $0)) (field (mut v128)) (field v128) (field (mut (ref (shared eq)))) (field (ref null $1))))))
40+
;; CHECK-NEXT: (type $2 (sub (func (result i64))))
41+
;; CHECK-NEXT: (type $3 (shared (struct (field i64) (field (ref null (shared i31))) (field i16) (field (mut i8)) (field (mut (ref null (shared any)))))))
42+
;; CHECK-NEXT: (type $4 (sub (func (param (ref $3) eqref) (result (ref null $4)))))
4343
;; CHECK-NEXT: )
4444
;; CHECK-NEXT: (rec
45-
;; CHECK-NEXT: (type $5 (struct (field (mut (ref $0))) (field i8) (field i31ref) (field (ref func)) (field f32)))
46-
;; CHECK-NEXT: (type $6 (sub (func (param f64 (ref $7) (ref null $5) f32) (result i32))))
47-
;; CHECK-NEXT: (type $7 (sub (shared (func (param (ref null $4) (ref null $6))))))
45+
;; CHECK-NEXT: (type $5 (struct (field (mut v128))))
46+
;; CHECK-NEXT: (type $6 (sub (func (param f64 (ref $1)) (result v128))))
47+
;; CHECK-NEXT: (type $7 (sub (shared (func (param (ref null $4) (ref $0) (ref $2) i32 v128) (result (ref null $1))))))
4848
;; CHECK-NEXT: )
4949
;; CHECK-NEXT: (rec
50-
;; CHECK-NEXT: (type $8 (struct (field (mut v128)) (field (mut (ref $5))) (field (mut v128)) (field (mut f64)) (field (mut i8))))
51-
;; CHECK-NEXT: (type $9 (shared (struct (field i32) (field (mut i32)) (field (mut i16)))))
52-
;; CHECK-NEXT: (type $10 (sub (func (param f32 (ref null $4) f64) (result (ref (shared struct))))))
50+
;; CHECK-NEXT: (type $8 (struct (field (mut (ref $5))) (field v128)))
51+
;; CHECK-NEXT: (type $9 (sub (shared (struct (field i64)))))
52+
;; CHECK-NEXT: (type $10 (sub (func (result v128))))
5353
;; CHECK-NEXT: )
5454
;; CHECK-NEXT: (rec
55-
;; CHECK-NEXT: (type $11 (cont $16))
56-
;; CHECK-NEXT: (type $12 (sub final $10 (func (param f32 funcref f64) (result (ref $9)))))
57-
;; CHECK-NEXT: (type $13 (cont $19))
58-
;; CHECK-NEXT: (type $14 (sub $2 (func (param (ref null (shared struct))))))
59-
;; CHECK-NEXT: (type $15 (sub (struct (field (mut (ref $7))) (field (mut i8)) (field (mut v128)) (field f64) (field f64) (field i64))))
60-
;; CHECK-NEXT: (type $16 (sub final $14 (func (param (ref null (shared eq))))))
61-
;; CHECK-NEXT: (type $17 (sub (func (param f32 v128 (ref $13)) (result (ref $5)))))
62-
;; CHECK-NEXT: (type $18 (struct (field (ref null $6)) (field (ref $17)) (field (mut f64)) (field (mut i64))))
63-
;; CHECK-NEXT: (type $19 (sub $4 (func (result i64))))
55+
;; CHECK-NEXT: (type $11 (sub (cont $6)))
56+
;; CHECK-NEXT: (type $12 (sub $10 (func (result v128))))
57+
;; CHECK-NEXT: (type $13 (cont $10))
58+
;; CHECK-NEXT: (type $14 (sub $2 (func (result i64))))
59+
;; CHECK-NEXT: (type $15 (sub (struct (field (mut (ref $3))) (field (mut (ref null $19))) (field i64) (field (ref $13)) (field i8) (field (mut i16)))))
60+
;; CHECK-NEXT: (type $16 (sub $14 (func (result i64))))
61+
;; CHECK-NEXT: (type $17 (sub (func (result f64))))
62+
;; CHECK-NEXT: (type $18 (struct (field f32) (field (mut (ref $6))) (field (mut f64)) (field f32)))
63+
;; CHECK-NEXT: (type $19 (sub $4 (func (param (ref (shared any)) anyref) (result (ref null $19)))))
6464
;; CHECK-NEXT: )

0 commit comments

Comments
 (0)