Skip to content

Commit a070616

Browse files
authored
Generate atomic array operations in the fuzzer (#8545)
Including both atomic get/set operations and RMWs.
1 parent 7ee433f commit a070616

File tree

2 files changed

+98
-11
lines changed

2 files changed

+98
-11
lines changed

src/tools/fuzzing.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,8 @@ class TranslateToFuzzReader {
545545
Expression* makeStructSet(Type type);
546546
Expression* makeArrayGet(Type type);
547547
Expression* makeArraySet(Type type);
548+
Expression* makeArrayRMW(Type type);
549+
Expression* makeArrayCmpxchg(Type type);
548550
// Use a single method for the misc array operations, to not give them too
549551
// much representation (e.g. compared to struct operations, which only include
550552
// get/set).

src/tools/fuzzing/fuzzing.cpp

Lines changed: 96 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2608,6 +2608,12 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) {
26082608
if (typeArrays.find(type) != typeArrays.end()) {
26092609
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
26102610
&Self::makeArrayGet);
2611+
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC |
2612+
FeatureSet::Atomics | FeatureSet::SharedEverything,
2613+
&Self::makeArrayRMW);
2614+
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC |
2615+
FeatureSet::Atomics | FeatureSet::SharedEverything,
2616+
&Self::makeArrayCmpxchg);
26112617
}
26122618
}
26132619
return (this->*pick(options))(type);
@@ -5726,19 +5732,23 @@ Expression* TranslateToFuzzReader::makeArrayGet(Type type) {
57265732
auto* ref = makeTrappingRefUse(arrayType);
57275733
auto* index = make(Type::i32);
57285734
auto signed_ = maybeSignedGet(arrayType.getArray().element);
5735+
auto order = MemoryOrder::Unordered;
5736+
if (wasm.features.hasAtomics() && wasm.features.hasSharedEverything() &&
5737+
oneIn(2)) {
5738+
order = oneIn(2) ? MemoryOrder::SeqCst : MemoryOrder::AcqRel;
5739+
}
57295740
// Only rarely emit a plain get which might trap. See related logic in
57305741
// ::makePointer().
57315742
if (allowOOB && oneIn(10)) {
5732-
return builder.makeArrayGet(
5733-
ref, index, MemoryOrder::Unordered, type, signed_);
5743+
return builder.makeArrayGet(ref, index, order, type, signed_);
57345744
}
57355745
// To avoid a trap, check the length dynamically using this pattern:
57365746
//
57375747
// index < array.len ? array[index] : ..some fallback value..
57385748
//
57395749
auto check = makeArrayBoundsCheck(ref, index, funcContext->func, builder);
5740-
auto* get = builder.makeArrayGet(
5741-
check.getRef, check.getIndex, MemoryOrder::Unordered, type, signed_);
5750+
auto* get =
5751+
builder.makeArrayGet(check.getRef, check.getIndex, order, type, signed_);
57425752
auto* fallback = makeTrivial(type);
57435753
return builder.makeIf(check.condition, get, fallback);
57445754
}
@@ -5753,21 +5763,96 @@ Expression* TranslateToFuzzReader::makeArraySet(Type type) {
57535763
auto* index = make(Type::i32);
57545764
auto* ref = makeTrappingRefUse(arrayType);
57555765
auto* value = make(elementType);
5766+
auto order = MemoryOrder::Unordered;
5767+
if (wasm.features.hasAtomics() && wasm.features.hasSharedEverything() &&
5768+
oneIn(2)) {
5769+
order = oneIn(2) ? MemoryOrder::SeqCst : MemoryOrder::AcqRel;
5770+
}
57565771
// Only rarely emit a plain get which might trap. See related logic in
57575772
// ::makePointer().
57585773
if (allowOOB && oneIn(10)) {
5759-
return builder.makeArraySet(ref, index, value, MemoryOrder::Unordered);
5774+
return builder.makeArraySet(ref, index, value, order);
57605775
}
5761-
// To avoid a trap, check the length dynamically using this pattern:
5762-
//
5763-
// if (index < array.len) array[index] = value;
5764-
//
5776+
// To avoid a trap, check the length dynamically.
57655777
auto check = makeArrayBoundsCheck(ref, index, funcContext->func, builder);
5766-
auto* set = builder.makeArraySet(
5767-
check.getRef, check.getIndex, value, MemoryOrder::Unordered);
5778+
auto* set = builder.makeArraySet(check.getRef, check.getIndex, value, order);
57685779
return builder.makeIf(check.condition, set);
57695780
}
57705781

5782+
Expression* TranslateToFuzzReader::makeArrayRMW(Type type) {
5783+
bool isAny =
5784+
type.isRef() &&
5785+
HeapType(type.getHeapType().getTop()).isMaybeShared(HeapType::any);
5786+
if (type != Type::i32 && type != Type::i64 && !isAny) {
5787+
// Not a valid element type for an RMW operation.
5788+
return makeArrayGet(type);
5789+
}
5790+
auto& arrays = typeArrays[type];
5791+
assert(!arrays.empty());
5792+
auto arrayType = pick(arrays);
5793+
const auto& element = arrayType.getArray().element;
5794+
if (element.isPacked() || element.mutable_ == Immutable) {
5795+
// Cannot RMW a packed or immutable field.
5796+
return makeArrayGet(type);
5797+
}
5798+
AtomicRMWOp op = RMWXchg;
5799+
if (type == Type::i32 || type == Type::i64) {
5800+
op = pick(RMWAdd, RMWSub, RMWAnd, RMWOr, RMWXor, RMWXchg);
5801+
}
5802+
auto* ref = makeTrappingRefUse(arrayType);
5803+
auto* index = make(Type::i32);
5804+
auto* value = make(type);
5805+
auto order = oneIn(2) ? MemoryOrder::SeqCst : MemoryOrder::AcqRel;
5806+
// Only rarely emit a plain operation which might trap. See related logic in
5807+
// ::makePointer().
5808+
if (allowOOB && oneIn(10)) {
5809+
return builder.makeArrayRMW(op, ref, index, value, order);
5810+
}
5811+
// To avoid a trap, check the length dynamically.
5812+
auto check = makeArrayBoundsCheck(ref, index, funcContext->func, builder);
5813+
auto* rmw =
5814+
builder.makeArrayRMW(op, check.getRef, check.getIndex, value, order);
5815+
auto* fallback = makeTrivial(type);
5816+
return builder.makeIf(check.condition, rmw, fallback);
5817+
}
5818+
5819+
Expression* TranslateToFuzzReader::makeArrayCmpxchg(Type type) {
5820+
bool isShared = type.isRef() && type.getHeapType().isShared();
5821+
Type eq(HeapTypes::eq.getBasic(isShared ? Shared : Unshared), Nullable);
5822+
bool isEq = Type::isSubType(type, eq);
5823+
if (type != Type::i32 && type != Type::i64 && !isEq) {
5824+
// Not a valid element type for a cmpxchg operation.
5825+
return makeArrayGet(type);
5826+
}
5827+
auto& arrays = typeArrays[type];
5828+
assert(!arrays.empty());
5829+
auto arrayType = pick(arrays);
5830+
const auto& element = arrayType.getArray().element;
5831+
if (element.isPacked() || element.mutable_ == Immutable) {
5832+
// Cannot RMW a packed or immutable field.
5833+
return makeArrayGet(type);
5834+
}
5835+
auto* ref = makeTrappingRefUse(arrayType);
5836+
auto* index = make(Type::i32);
5837+
// For reference fields, expected can be a subtype of eq. Only use the extra
5838+
// flexibility occasionally because it makes the expected value less likely to
5839+
// be equal to the actual value.
5840+
auto* expected = make(isEq && oneIn(4) ? eq : type);
5841+
auto* replacement = make(type);
5842+
auto order = oneIn(2) ? MemoryOrder::SeqCst : MemoryOrder::AcqRel;
5843+
// Only rarely emit a plain operation which might trap. See related logic in
5844+
// ::makePointer().
5845+
if (allowOOB && oneIn(10)) {
5846+
return builder.makeArrayCmpxchg(ref, index, expected, replacement, order);
5847+
}
5848+
// To avoid a trap, check the length dynamically.
5849+
auto check = makeArrayBoundsCheck(ref, index, funcContext->func, builder);
5850+
auto* cmpxchg = builder.makeArrayCmpxchg(
5851+
check.getRef, check.getIndex, expected, replacement, order);
5852+
auto* fallback = makeTrivial(type);
5853+
return builder.makeIf(check.condition, cmpxchg, fallback);
5854+
}
5855+
57715856
Expression* TranslateToFuzzReader::makeArrayBulkMemoryOp(Type type) {
57725857
assert(type == Type::none);
57735858
if (mutableArrays.empty()) {

0 commit comments

Comments
 (0)