Skip to content

Commit 848a289

Browse files
authored
[threads] Update the fuzzer for shared types (#6771)
Update the fuzzer to both handle shared types in initial contents and create and use new shared types without crashing or producing invalid modules. Since V8 does not have a complete implementation of shared-everything-threads yet, disable fuzzing V8 when shared-everything is enabled. To avoid losing too much coverage of V8, disable shared-everything in the fuzzer more frequently than other features.
1 parent 5a6eb16 commit 848a289

File tree

4 files changed

+136
-117
lines changed

4 files changed

+136
-117
lines changed

scripts/fuzz_opt.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,11 @@ def randomize_feature_opts():
145145
# 2/3 of the remaining 90% use them all. This is useful to maximize
146146
# coverage, as enabling more features enables more optimizations and
147147
# code paths, and also allows all initial contents to run.
148-
pass
148+
149+
# The shared-everything feature is new and we want to fuzz it, but it
150+
# also currently disables fuzzing V8, so disable it most of the time.
151+
if random.random() < 0.9:
152+
FEATURE_OPTS.append('--disable-shared-everything')
149153

150154
print('randomized feature opts:', '\n ' + '\n '.join(FEATURE_OPTS))
151155

@@ -350,21 +354,6 @@ def is_git_repo():
350354
'exception-handling.wast',
351355
'translate-to-new-eh.wast',
352356
'rse-eh.wast',
353-
# Shared types implementation in progress
354-
'type-merging-shared.wast',
355-
'shared-types.wast',
356-
'shared-polymorphism.wast',
357-
'shared-struct.wast',
358-
'shared-array.wast',
359-
'shared-i31.wast',
360-
'shared-null.wast',
361-
'shared-absheaptype.wast',
362-
'type-ssa-shared.wast',
363-
'shared-ref_eq.wast',
364-
'shared-types-no-gc.wast',
365-
'shared-ref-i31.wast',
366-
'table-type.wast',
367-
'elem-type.wast',
368357
]
369358

370359

@@ -847,7 +836,10 @@ def run(self, wasm, extra_d8_flags=[]):
847836
return run_vm([shared.V8, FUZZ_SHELL_JS] + shared.V8_OPTS + extra_d8_flags + ['--', wasm])
848837

849838
def can_run(self, wasm):
850-
return True
839+
# V8 does not support shared memories when running with
840+
# shared-everything enabled, so do not fuzz shared-everything
841+
# for now.
842+
return all_disallowed(['shared-everything'])
851843

852844
def can_compare_to_self(self):
853845
# With nans, VM differences can confuse us, so only very simple VMs

src/tools/fuzzing/fuzzing.cpp

Lines changed: 88 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,8 @@ void TranslateToFuzzReader::setupHeapTypes() {
246246

247247
// For GC, also generate random types.
248248
if (wasm.features.hasGC()) {
249-
// Do not generate shared types until the fuzzer can be updated to handle
250-
// them.
251-
auto features = wasm.features - FeatureSet::SharedEverything;
252249
auto generator =
253-
HeapTypeGenerator::create(random, features, upTo(MAX_NEW_GC_TYPES));
250+
HeapTypeGenerator::create(random, wasm.features, upTo(MAX_NEW_GC_TYPES));
254251
auto result = generator.builder.build();
255252
if (auto* err = result.getError()) {
256253
Fatal() << "Failed to build heap types: " << err->reason << " at index "
@@ -288,10 +285,16 @@ void TranslateToFuzzReader::setupHeapTypes() {
288285
}
289286
// Basic types must be handled directly, since subTypes doesn't look at
290287
// those.
288+
auto share = type.getShared();
289+
auto struct_ = HeapTypes::struct_.getBasic(share);
290+
auto array = HeapTypes::array.getBasic(share);
291+
auto eq = HeapTypes::eq.getBasic(share);
292+
auto any = HeapTypes::any.getBasic(share);
293+
auto func = HeapTypes::func.getBasic(share);
291294
if (type.isStruct()) {
292-
interestingHeapSubTypes[HeapType::struct_].push_back(type);
293-
interestingHeapSubTypes[HeapType::eq].push_back(type);
294-
interestingHeapSubTypes[HeapType::any].push_back(type);
295+
interestingHeapSubTypes[struct_].push_back(type);
296+
interestingHeapSubTypes[eq].push_back(type);
297+
interestingHeapSubTypes[any].push_back(type);
295298

296299
// Note the mutable fields.
297300
auto& fields = type.getStruct().fields;
@@ -301,15 +304,15 @@ void TranslateToFuzzReader::setupHeapTypes() {
301304
}
302305
}
303306
} else if (type.isArray()) {
304-
interestingHeapSubTypes[HeapType::array].push_back(type);
305-
interestingHeapSubTypes[HeapType::eq].push_back(type);
306-
interestingHeapSubTypes[HeapType::any].push_back(type);
307+
interestingHeapSubTypes[array].push_back(type);
308+
interestingHeapSubTypes[eq].push_back(type);
309+
interestingHeapSubTypes[any].push_back(type);
307310

308311
if (type.getArray().element.mutable_) {
309312
mutableArrays.push_back(type);
310313
}
311314
} else if (type.isSignature()) {
312-
interestingHeapSubTypes[HeapType::func].push_back(type);
315+
interestingHeapSubTypes[func].push_back(type);
313316
}
314317
}
315318

@@ -2468,11 +2471,13 @@ Literal TranslateToFuzzReader::makeLiteral(Type type) {
24682471

24692472
Expression* TranslateToFuzzReader::makeRefFuncConst(Type type) {
24702473
auto heapType = type.getHeapType();
2474+
auto share = heapType.getShared();
24712475
if (heapType.isBasic()) {
24722476
assert(heapType.getBasic(Unshared) == HeapType::func);
24732477
// With high probability, use the last created function if possible.
24742478
// Otherwise, continue on to select some other function.
2475-
if (funcContext && !oneIn(4)) {
2479+
if (funcContext && funcContext->func->type.getShared() == share &&
2480+
!oneIn(4)) {
24762481
auto* target = funcContext->func;
24772482
return builder.makeRefFunc(target->name, target->type);
24782483
}
@@ -2496,7 +2501,7 @@ Expression* TranslateToFuzzReader::makeRefFuncConst(Type type) {
24962501
// here).
24972502
if ((type.isNullable() && oneIn(2)) ||
24982503
(type.isNonNullable() && oneIn(16) && funcContext)) {
2499-
Expression* ret = builder.makeRefNull(HeapType::nofunc);
2504+
Expression* ret = builder.makeRefNull(HeapTypes::nofunc.getBasic(share));
25002505
if (!type.isNullable()) {
25012506
assert(funcContext);
25022507
ret = builder.makeRefAs(RefAsNonNull, ret);
@@ -2511,7 +2516,10 @@ Expression* TranslateToFuzzReader::makeRefFuncConst(Type type) {
25112516
if (heapType.isBasic()) {
25122517
// We need a specific signature type to create a function. Pick an arbitrary
25132518
// signature if we only had generic 'func' here.
2514-
heapType = Signature(Type::none, Type::none);
2519+
TypeBuilder builder(1);
2520+
builder[0] = Signature(Type::none, Type::none);
2521+
builder[0].setShared(share);
2522+
heapType = (*builder.build())[0];
25152523
}
25162524
auto* body = heapType.getSignature().results == Type::none
25172525
? (Expression*)builder.makeNop()
@@ -2553,10 +2561,10 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
25532561
auto heapType = type.getHeapType();
25542562
assert(heapType.isBasic());
25552563
assert(wasm.features.hasReferenceTypes());
2556-
assert(!heapType.isShared() && "TODO: handle shared types");
2564+
auto share = heapType.getShared();
25572565
switch (heapType.getBasic(Unshared)) {
25582566
case HeapType::ext: {
2559-
auto null = builder.makeRefNull(HeapType::ext);
2567+
auto null = builder.makeRefNull(HeapTypes::ext.getBasic(share));
25602568
// TODO: support actual non-nullable externrefs via imported globals or
25612569
// similar.
25622570
if (!type.isNullable()) {
@@ -2575,12 +2583,16 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
25752583
// Choose a subtype we can materialize a constant for. We cannot
25762584
// materialize non-nullable refs to func or i31 in global contexts.
25772585
Nullability nullability = getSubType(type.getNullability());
2578-
auto subtype = pick(FeatureOptions<HeapType>()
2579-
.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
2580-
HeapType::i31,
2581-
HeapType::struct_,
2582-
HeapType::array)
2583-
.add(FeatureSet::Strings, HeapType::string));
2586+
auto subtypeOpts = FeatureOptions<HeapType>().add(
2587+
FeatureSet::ReferenceTypes | FeatureSet::GC,
2588+
HeapType::i31,
2589+
HeapType::struct_,
2590+
HeapType::array);
2591+
if (share == Unshared) {
2592+
// Shared strings not yet supported.
2593+
subtypeOpts.add(FeatureSet::Strings, HeapType::string);
2594+
}
2595+
auto subtype = pick(subtypeOpts).getBasic(share);
25842596
return makeConst(Type(subtype, nullability));
25852597
}
25862598
case HeapType::eq: {
@@ -2589,7 +2601,7 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
25892601
// a subtype of anyref, but we cannot create constants of it, except
25902602
// for null.
25912603
assert(type.isNullable());
2592-
return builder.makeRefNull(HeapType::none);
2604+
return builder.makeRefNull(HeapTypes::none.getBasic(share));
25932605
}
25942606
auto nullability = getSubType(type.getNullability());
25952607
// ref.i31 is not allowed in initializer expressions.
@@ -2605,14 +2617,14 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
26052617
subtype = HeapType::array;
26062618
break;
26072619
}
2608-
return makeConst(Type(subtype, nullability));
2620+
return makeConst(Type(subtype.getBasic(share), nullability));
26092621
}
26102622
case HeapType::i31: {
26112623
assert(wasm.features.hasGC());
26122624
if (type.isNullable() && oneIn(4)) {
2613-
return builder.makeRefNull(HeapType::none);
2625+
return builder.makeRefNull(HeapTypes::none.getBasic(share));
26142626
}
2615-
return builder.makeRefI31(makeConst(Type::i32));
2627+
return builder.makeRefI31(makeConst(Type::i32), share);
26162628
}
26172629
case HeapType::struct_: {
26182630
assert(wasm.features.hasGC());
@@ -2621,15 +2633,29 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
26212633
// Use a local static to avoid the expense of canonicalizing a new type
26222634
// every time.
26232635
static HeapType trivialStruct = HeapType(Struct());
2624-
return builder.makeStructNew(trivialStruct, std::vector<Expression*>{});
2636+
static HeapType sharedTrivialStruct = []() {
2637+
TypeBuilder builder(1);
2638+
builder[0] = Struct{};
2639+
builder[0].setShared();
2640+
return (*builder.build())[0];
2641+
}();
2642+
auto ht = share == Shared ? sharedTrivialStruct : trivialStruct;
2643+
return builder.makeStructNew(ht, std::vector<Expression*>{});
26252644
}
26262645
case HeapType::array: {
26272646
static HeapType trivialArray =
26282647
HeapType(Array(Field(Field::PackedType::i8, Immutable)));
2629-
return builder.makeArrayNewFixed(trivialArray, {});
2648+
static HeapType sharedTrivialArray = []() {
2649+
TypeBuilder builder(1);
2650+
builder[0] = Array(Field(Field::PackedType::i8, Immutable));
2651+
builder[0].setShared();
2652+
return (*builder.build())[0];
2653+
}();
2654+
auto ht = share == Shared ? sharedTrivialArray : trivialArray;
2655+
return builder.makeArrayNewFixed(ht, {});
26302656
}
26312657
case HeapType::exn: {
2632-
auto null = builder.makeRefNull(HeapType::exn);
2658+
auto null = builder.makeRefNull(HeapTypes::exn.getBasic(share));
26332659
if (!type.isNullable()) {
26342660
assert(funcContext);
26352661
return builder.makeRefAs(RefAsNonNull, null);
@@ -2638,6 +2664,7 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
26382664
}
26392665
case HeapType::string: {
26402666
// In non-function contexts all we can do is string.const.
2667+
assert(share == Unshared && "shared strings not supported");
26412668
if (!funcContext) {
26422669
return makeStringConst();
26432670
}
@@ -2671,7 +2698,7 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
26712698
case HeapType::nofunc:
26722699
case HeapType::nocont:
26732700
case HeapType::noexn: {
2674-
auto null = builder.makeRefNull(heapType);
2701+
auto null = builder.makeRefNull(heapType.getBasic(share));
26752702
if (!type.isNullable()) {
26762703
assert(funcContext);
26772704
return builder.makeRefAs(RefAsNonNull, null);
@@ -4231,48 +4258,56 @@ HeapType TranslateToFuzzReader::getSubType(HeapType type) {
42314258
return type;
42324259
}
42334260
if (type.isBasic() && oneIn(2)) {
4234-
assert(!type.isShared() && "TODO: handle shared types");
4261+
auto share = type.getShared();
42354262
switch (type.getBasic(Unshared)) {
42364263
case HeapType::func:
42374264
// TODO: Typed function references.
42384265
return pick(FeatureOptions<HeapType>()
42394266
.add(FeatureSet::ReferenceTypes, HeapType::func)
4240-
.add(FeatureSet::GC, HeapType::nofunc));
4267+
.add(FeatureSet::GC, HeapType::nofunc))
4268+
.getBasic(share);
42414269
case HeapType::cont:
4242-
return pick(HeapType::cont, HeapType::nocont);
4270+
return pick(HeapTypes::cont, HeapTypes::nocont).getBasic(share);
42434271
case HeapType::ext:
42444272
return pick(FeatureOptions<HeapType>()
42454273
.add(FeatureSet::ReferenceTypes, HeapType::ext)
4246-
.add(FeatureSet::GC, HeapType::noext));
4247-
case HeapType::any:
4274+
.add(FeatureSet::GC, HeapType::noext))
4275+
.getBasic(share);
4276+
case HeapType::any: {
42484277
assert(wasm.features.hasReferenceTypes());
42494278
assert(wasm.features.hasGC());
4250-
return pick(FeatureOptions<HeapType>()
4251-
.add(FeatureSet::GC,
4252-
HeapType::any,
4253-
HeapType::eq,
4254-
HeapType::i31,
4255-
HeapType::struct_,
4256-
HeapType::array,
4257-
HeapType::none)
4258-
.add(FeatureSet::Strings, HeapType::string));
4279+
auto options = FeatureOptions<HeapType>().add(FeatureSet::GC,
4280+
HeapType::any,
4281+
HeapType::eq,
4282+
HeapType::i31,
4283+
HeapType::struct_,
4284+
HeapType::array,
4285+
HeapType::none);
4286+
if (share == Unshared) {
4287+
// Shared strings not yet supported.
4288+
options.add(FeatureSet::Strings, HeapType::string);
4289+
}
4290+
return pick(options).getBasic(share);
4291+
}
42594292
case HeapType::eq:
42604293
assert(wasm.features.hasReferenceTypes());
42614294
assert(wasm.features.hasGC());
4262-
return pick(HeapType::eq,
4263-
HeapType::i31,
4264-
HeapType::struct_,
4265-
HeapType::array,
4266-
HeapType::none);
4295+
return pick(HeapTypes::eq,
4296+
HeapTypes::i31,
4297+
HeapTypes::struct_,
4298+
HeapTypes::array,
4299+
HeapTypes::none)
4300+
.getBasic(share);
42674301
case HeapType::i31:
4268-
return pick(HeapType::i31, HeapType::none);
4302+
return pick(HeapTypes::i31, HeapTypes::none).getBasic(share);
42694303
case HeapType::struct_:
4270-
return pick(HeapType::struct_, HeapType::none);
4304+
return pick(HeapTypes::struct_, HeapTypes::none).getBasic(share);
42714305
case HeapType::array:
4272-
return pick(HeapType::array, HeapType::none);
4306+
return pick(HeapTypes::array, HeapTypes::none).getBasic(share);
42734307
case HeapType::exn:
4274-
return HeapType::exn;
4308+
return HeapTypes::exn.getBasic(share);
42754309
case HeapType::string:
4310+
assert(share == Unshared);
42764311
return HeapType::string;
42774312
case HeapType::none:
42784313
case HeapType::noext:

src/tools/fuzzing/heap-types.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,8 @@ void Inhabitator::markExternRefsNullable() {
759759
auto children = type.getTypeChildren();
760760
for (size_t i = 0; i < children.size(); ++i) {
761761
auto child = children[i];
762-
if (child.isRef() && child.getHeapType() == HeapType::ext &&
762+
if (child.isRef() && child.getHeapType().isBasic() &&
763+
child.getHeapType().getBasic(Unshared) == HeapType::ext &&
763764
child.isNonNullable()) {
764765
markNullable({type, i});
765766
}

0 commit comments

Comments
 (0)