Skip to content

Commit 7664807

Browse files
authored
[GC][EH] Fuzz exnref in GC structs (#7288)
Most of the work here is avoiding shared and non-nullable exnref in various places, as we cannot support those in the fuzzer (I don't see a way to generate shared exnrefs, and non-nullable ones can be created but not in global places, as the way to create them needs a block and a try).
1 parent 0be9bfe commit 7664807

3 files changed

Lines changed: 60 additions & 30 deletions

File tree

src/tools/fuzzing/fuzzing.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,8 +1857,10 @@ Expression* TranslateToFuzzReader::make(Type type) {
18571857
assert(type == Type::unreachable);
18581858
ret = _makeunreachable();
18591859
}
1860-
// We should create the right type of thing.
1861-
assert(Type::isSubType(ret->type, type));
1860+
if (!Type::isSubType(ret->type, type)) {
1861+
Fatal() << "Did not generate the right subtype of " << type
1862+
<< ", instead we have " << ret->type << " : " << *ret << '\n';
1863+
}
18621864
nesting--;
18631865
return ret;
18641866
}
@@ -3273,13 +3275,13 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
32733275
return builder.makeArrayNewFixed(ht, {});
32743276
}
32753277
case HeapType::exn: {
3276-
// If nullable, we can emit a null. If not, generate an exnref using a
3277-
// throw in a try_table.
3278-
if (type.isNullable() && oneIn(2)) {
3278+
// If nullable, we can emit a null. If there is no function context, then
3279+
// we must do so, as the other option is a throw in a block, which are not
3280+
// possible outside of functions.
3281+
if ((type.isNullable() && oneIn(2)) || !funcContext) {
32793282
return builder.makeRefNull(HeapTypes::exn.getBasic(share));
32803283
}
32813284

3282-
// Make a catch_all_ref to a block.
32833285
auto* throww = makeThrow(Type::unreachable);
32843286
auto label = makeLabel();
32853287
auto* tryy = builder.makeTryTable(throww, {Name()}, {label}, {true});
@@ -5101,6 +5103,7 @@ HeapType TranslateToFuzzReader::getSubType(HeapType type) {
51015103
case HeapType::array:
51025104
return pick(HeapTypes::array, HeapTypes::none).getBasic(share);
51035105
case HeapType::exn:
5106+
assert(share == Unshared);
51045107
return HeapTypes::exn.getBasic(share);
51055108
case HeapType::string:
51065109
assert(share == Unshared);
@@ -5133,7 +5136,13 @@ Type TranslateToFuzzReader::getSubType(Type type) {
51335136
}
51345137
return Type(types);
51355138
} else if (type.isRef()) {
5136-
auto heapType = getSubType(type.getHeapType());
5139+
auto heapType = type.getHeapType();
5140+
// Do not generate non-nullable exnrefs in global positions (they cannot be
5141+
// created in wasm, nor imported from JS).
5142+
if (!funcContext && heapType.isMaybeShared(HeapType::exn)) {
5143+
return type;
5144+
}
5145+
heapType = getSubType(heapType);
51375146
auto nullability = getSubType(type.getNullability());
51385147
auto subType = Type(heapType, nullability);
51395148
// We don't want to emit lots of uninhabitable types like (ref none), so

src/tools/fuzzing/heap-types.cpp

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -154,20 +154,27 @@ struct HeapTypeGeneratorImpl {
154154

155155
HeapType::BasicHeapType generateBasicHeapType(Shareability share) {
156156
// Choose bottom types more rarely.
157-
// TODO: string, exn, and cont types
157+
// TODO: string and cont types
158158
if (rand.oneIn(16)) {
159159
HeapType ht =
160160
rand.pick(HeapType::noext, HeapType::nofunc, HeapType::none);
161161
return ht.getBasic(share);
162162
}
163-
HeapType ht = rand.pick(HeapType::func,
164-
HeapType::ext,
165-
HeapType::any,
166-
HeapType::eq,
167-
HeapType::i31,
168-
HeapType::struct_,
169-
HeapType::array);
170-
if (share == Unshared && features.hasSharedEverything() && rand.oneIn(2)) {
163+
164+
std::vector<HeapType> options{HeapType::func,
165+
HeapType::ext,
166+
HeapType::any,
167+
HeapType::eq,
168+
HeapType::i31,
169+
HeapType::struct_,
170+
HeapType::array};
171+
// Avoid shared exn, which we cannot generate.
172+
if (features.hasExceptionHandling() && share == Unshared) {
173+
options.push_back(HeapType::exn);
174+
}
175+
auto ht = rand.pick(options);
176+
if (share == Unshared && features.hasSharedEverything() &&
177+
ht != HeapType::exn && rand.oneIn(2)) {
171178
share = Shared;
172179
}
173180
return ht.getBasic(share);
@@ -203,7 +210,15 @@ struct HeapTypeGeneratorImpl {
203210

204211
Type generateRefType(Shareability share) {
205212
auto heapType = generateHeapType(share);
206-
auto nullability = rand.oneIn(2) ? Nullable : NonNullable;
213+
Nullability nullability;
214+
if (heapType.isMaybeShared(HeapType::exn)) {
215+
// Do not generate non-nullable exnrefs for now, as we cannot generate
216+
// them in global positions (they cannot be created in wasm, nor imported
217+
// from JS).
218+
nullability = Nullable;
219+
} else {
220+
nullability = rand.oneIn(2) ? Nullable : NonNullable;
221+
}
207222
return builder.getTempRefType(heapType, nullability);
208223
}
209224

@@ -521,6 +536,12 @@ struct HeapTypeGeneratorImpl {
521536
};
522537

523538
Ref generateSubRef(Ref super) {
539+
if (super.type.isMaybeShared(HeapType::exn)) {
540+
// Do not generate non-nullable exnrefs for now, as we cannot generate
541+
// them in global positions (they cannot be created in wasm, nor imported
542+
// from JS). There are also no subtypes to consider, so just return.
543+
return super;
544+
}
524545
auto nullability = super.nullability == NonNullable
525546
? NonNullable
526547
: rand.oneIn(2) ? Nullable : NonNullable;

test/lit/fuzz-types.test

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
;; CHECK-NEXT: Built 20 types:
55
;; CHECK-NEXT: (rec
66
;; CHECK-NEXT: (type $0 (sub (struct (field (mut i16)) (field (mut (ref $2))) (field (mut (ref null $2))))))
7-
;; CHECK-NEXT: (type $1 (sub (func (param (ref $1)) (result f64 (ref $0) f32 (ref null (shared eq))))))
7+
;; CHECK-NEXT: (type $1 (sub (func (param (ref $1)) (result f64 (ref $0) f32 structref))))
88
;; CHECK-NEXT: (type $2 (sub (shared (struct (field (mut (ref null (shared extern)))) (field (mut (ref null $2)))))))
99
;; CHECK-NEXT: (type $3 (sub (shared (struct))))
1010
;; CHECK-NEXT: )
@@ -22,21 +22,21 @@
2222
;; CHECK-NEXT: (type $12 (sub (shared (array (ref $3)))))
2323
;; CHECK-NEXT: (type $13 (sub (shared (func (param (ref null $19) v128) (result (ref null $12))))))
2424
;; CHECK-NEXT: (type $14 (sub final $12 (shared (array (ref $3)))))
25-
;; CHECK-NEXT: (type $15 (sub (shared (func (param (ref null (shared struct)) i31ref) (result nullfuncref)))))
25+
;; CHECK-NEXT: (type $15 (sub (shared (func (param i31ref (ref $5)) (result i32)))))
2626
;; CHECK-NEXT: (type $16 (sub $5 (array i32)))
27-
;; CHECK-NEXT: (type $17 (sub (func (param v128) (result f64))))
28-
;; CHECK-NEXT: (type $18 (sub (array (ref $11))))
29-
;; CHECK-NEXT: (type $19 (shared (array i8)))
27+
;; CHECK-NEXT: (type $17 (sub (func (result (ref $7)))))
28+
;; CHECK-NEXT: (type $18 (sub (array (mut i8))))
29+
;; CHECK-NEXT: (type $19 (shared (array v128)))
3030
;; CHECK-NEXT: )
31-
;; CHECK-NEXT:
31+
;; CHECK-NEXT:
3232
;; CHECK-NEXT: Inhabitable types:
33-
;; CHECK-NEXT:
33+
;; CHECK-NEXT:
3434
;; CHECK-NEXT: Built 20 types:
3535
;; CHECK-NEXT: (rec
3636
;; CHECK-NEXT: (type $0 (sub (struct (field (mut i16)) (field (mut (ref $2))) (field (mut (ref null $2))))))
37-
;; CHECK-NEXT: (type $1 (sub (func (param (ref $1)) (result f64 (ref $0) f32 (ref null (shared eq))))))
37+
;; CHECK-NEXT: (type $1 (sub (func (param (ref $1)) (result f64 (ref $0) f32 structref))))
3838
;; CHECK-NEXT: (type $2 (sub (shared (struct (field (mut (ref null (shared extern)))) (field (mut (ref null $2)))))))
39-
;; CHECK-NEXT: (type $3 (sub (shared (struct)))
39+
;; CHECK-NEXT: (type $3 (sub (shared (struct))))
4040
;; CHECK-NEXT: )
4141
;; CHECK-NEXT: (rec
4242
;; CHECK-NEXT: (type $4 (sub (array i32)))
@@ -52,9 +52,9 @@
5252
;; CHECK-NEXT: (type $12 (sub (shared (array (ref $3)))))
5353
;; CHECK-NEXT: (type $13 (sub (shared (func (param (ref null $19) v128) (result (ref null $12))))))
5454
;; CHECK-NEXT: (type $14 (sub final $12 (shared (array (ref $3)))))
55-
;; CHECK-NEXT: (type $15 (sub (shared (func (param (ref null (shared struct)) i31ref) (result nullfuncref)))))
55+
;; CHECK-NEXT: (type $15 (sub (shared (func (param i31ref (ref $5)) (result i32)))))
5656
;; CHECK-NEXT: (type $16 (sub $5 (array i32)))
57-
;; CHECK-NEXT: (type $17 (sub (func (param v128) (result f64))))
58-
;; CHECK-NEXT: (type $18 (sub (array (ref $11))))
59-
;; CHECK-NEXT: (type $19 (shared (array i8)))
57+
;; CHECK-NEXT: (type $17 (sub (func (result (ref $7)))))
58+
;; CHECK-NEXT: (type $18 (sub (array (mut i8))))
59+
;; CHECK-NEXT: (type $19 (shared (array v128)))
6060
;; CHECK-NEXT: )

0 commit comments

Comments
 (0)