Skip to content

Commit c8bfe28

Browse files
authored
Improve struct RMW validation and interpretation (#7216)
Add missing validation rules checking that accessed fields have allowed types. Copy the proposed upstream spec tests for this validation from WebAssembly/shared-everything-threads#85 with minor changes to account for differences in supported text format and to comment out unsupported tests. Also implement interpretation for the RMW instructions and add spec tests testing their execution. It is simpler to implement both validation and interpretation at once because the proposed upstream spec tests require both to pass.
1 parent 7453b1b commit c8bfe28

File tree

5 files changed

+986
-106
lines changed

5 files changed

+986
-106
lines changed

scripts/test/fuzzing.py

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
# the fuzzer does not support struct RMW ops
8282
'gc-atomics.wast',
8383
'gc-atomics-null-refs.wast',
84+
'shared-structs.wast',
8485
# contains too many segments to run in a wasm VM
8586
'limit-segments_disable-bulk-memory.wast',
8687
# https://github.com/WebAssembly/binaryen/issues/7176

src/wasm-interpreter.h

+64-2
Original file line numberDiff line numberDiff line change
@@ -1701,9 +1701,71 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
17011701
return Flow();
17021702
}
17031703

1704-
Flow visitStructRMW(StructRMW* curr) { WASM_UNREACHABLE("TODO"); }
1704+
Flow visitStructRMW(StructRMW* curr) {
1705+
NOTE_ENTER("StructRMW");
1706+
Flow ref = self()->visit(curr->ref);
1707+
if (ref.breaking()) {
1708+
return ref;
1709+
}
1710+
Flow value = self()->visit(curr->value);
1711+
if (value.breaking()) {
1712+
return value;
1713+
}
1714+
auto data = ref.getSingleValue().getGCData();
1715+
if (!data) {
1716+
trap("null ref");
1717+
}
1718+
auto& field = data->values[curr->index];
1719+
auto oldVal = field;
1720+
auto newVal = value.getSingleValue();
1721+
switch (curr->op) {
1722+
case RMWAdd:
1723+
field = field.add(newVal);
1724+
break;
1725+
case RMWSub:
1726+
field = field.sub(newVal);
1727+
break;
1728+
case RMWAnd:
1729+
field = field.and_(newVal);
1730+
break;
1731+
case RMWOr:
1732+
field = field.or_(newVal);
1733+
break;
1734+
case RMWXor:
1735+
field = field.xor_(newVal);
1736+
break;
1737+
case RMWXchg:
1738+
field = newVal;
1739+
break;
1740+
}
1741+
return oldVal;
1742+
}
17051743

1706-
Flow visitStructCmpxchg(StructCmpxchg* curr) { WASM_UNREACHABLE("TODO"); }
1744+
Flow visitStructCmpxchg(StructCmpxchg* curr) {
1745+
NOTE_ENTER("StructCmpxchg");
1746+
Flow ref = self()->visit(curr->ref);
1747+
if (ref.breaking()) {
1748+
return ref;
1749+
}
1750+
Flow expected = self()->visit(curr->expected);
1751+
if (expected.breaking()) {
1752+
return expected;
1753+
}
1754+
Flow replacement = self()->visit(curr->replacement);
1755+
if (replacement.breaking()) {
1756+
return replacement;
1757+
}
1758+
auto data = ref.getSingleValue().getGCData();
1759+
if (!data) {
1760+
trap("null ref");
1761+
}
1762+
auto& field = data->values[curr->index];
1763+
auto oldVal = field;
1764+
if (field == expected.getSingleValue()) {
1765+
field = replacement.getSingleValue();
1766+
}
1767+
return oldVal;
1768+
}
17071769

17081770
// Arbitrary deterministic limit on size. If we need to allocate a Literals
17091771
// vector that takes around 1-2GB of memory then we are likely to hit memory

src/wasm/wasm-validator.cpp

+31-4
Original file line numberDiff line numberDiff line change
@@ -3096,12 +3096,26 @@ void FunctionValidator::visitStructRMW(StructRMW* curr) {
30963096
return;
30973097
}
30983098
auto& field = fields[curr->index];
3099+
shouldBeEqual(
3100+
field.mutable_, Mutable, curr, "struct.atomic.rmw field must be mutable");
3101+
shouldBeFalse(
3102+
field.isPacked(), curr, "struct.atomic.rmw field must not be packed");
3103+
bool isAny =
3104+
field.type.isRef() &&
3105+
Type::isSubType(
3106+
field.type,
3107+
Type(HeapTypes::any.getBasic(field.type.getHeapType().getShared()),
3108+
Nullable));
3109+
if (!shouldBeTrue(field.type == Type::i32 || field.type == Type::i64 ||
3110+
(isAny && curr->op == RMWXchg),
3111+
curr,
3112+
"struct.atomic.rmw field type invalid for operation")) {
3113+
return;
3114+
}
30993115
shouldBeSubType(curr->value->type,
31003116
field.type,
31013117
curr,
31023118
"struct.atomic.rmw value must have the proper type");
3103-
shouldBeEqual(
3104-
field.mutable_, Mutable, curr, "struct.atomic.rmw field must be mutable");
31053119
}
31063120

31073121
void FunctionValidator::visitStructCmpxchg(StructCmpxchg* curr) {
@@ -3134,6 +3148,21 @@ void FunctionValidator::visitStructCmpxchg(StructCmpxchg* curr) {
31343148
return;
31353149
}
31363150
auto& field = fields[curr->index];
3151+
shouldBeEqual(
3152+
field.mutable_, Mutable, curr, "struct.atomic.rmw field must be mutable");
3153+
shouldBeFalse(
3154+
field.isPacked(), curr, "struct.atomic.rmw field must not be packed");
3155+
bool isEq =
3156+
field.type.isRef() &&
3157+
Type::isSubType(
3158+
field.type,
3159+
Type(HeapTypes::eq.getBasic(field.type.getHeapType().getShared()),
3160+
Nullable));
3161+
if (!shouldBeTrue(field.type == Type::i32 || field.type == Type::i64 || isEq,
3162+
curr,
3163+
"struct.atomic.rmw field type invalid for operation")) {
3164+
return;
3165+
}
31373166
shouldBeSubType(
31383167
curr->expected->type,
31393168
field.type,
@@ -3144,8 +3173,6 @@ void FunctionValidator::visitStructCmpxchg(StructCmpxchg* curr) {
31443173
field.type,
31453174
curr,
31463175
"struct.atomic.rmw.cmpxchg replacement value must have the proper type");
3147-
shouldBeEqual(
3148-
field.mutable_, Mutable, curr, "struct.atomic.rmw field must be mutable");
31493176
}
31503177

31513178
void FunctionValidator::visitArrayNew(ArrayNew* curr) {

test/spec/shared-struct.wast

-100
This file was deleted.

0 commit comments

Comments
 (0)