Skip to content

Commit 91269d8

Browse files
committed
Update testsuite
The two primary changes involved are: 1. Removal of `assert_return_canonical_nan`/`arithetic nan` in favor of special `nan:canonical`/`nan:arithmetic` constants that can only be used in test expectations. See: WebAssembly/spec#1104 2. New trapping behaviour for bulk memory operations. Range checks are now performed up front for opterations such as memory.fill and memory.copy. See: WebAssembly/bulk-memory-operations#111 And: WebAssembly/bulk-memory-operations#123 The old behaviour is still kept around to support table.fill which is defined in reference-types proposal and has yet to be updated. 3. nullref is now permitted in the text and binary format.
1 parent 82e64ba commit 91269d8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1374
-1511
lines changed

fuzz-in/wast.dict

-2
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,5 @@ op_assert_malformed="assert_malformed"
225225
op_assert_invalid="assert_invalid"
226226
op_assert_unlinkable="assert_unlinkable"
227227
op_assert_return="assert_return"
228-
op_assert_return_canonical_nan="assert_return_canonical_nan"
229-
op_assert_return_arithmetic_nan="assert_return_arithmetic_nan"
230228
op_assert_trap="assert_trap"
231229
op_assert_exhaustion="assert_exhaustion"

src/binary-writer-spec.cc

+18-28
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,6 @@ void BinaryWriterSpec::WriteCommandType(const Command& command) {
131131
"assert_uninstantiable",
132132
"assert_return",
133133
"assert_return_func",
134-
"assert_return_canonical_nan",
135-
"assert_return_arithmetic_nan",
136134
"assert_trap",
137135
"assert_exhaustion",
138136
};
@@ -189,7 +187,15 @@ void BinaryWriterSpec::WriteConst(const Const& const_) {
189187
WriteString("f32");
190188
WriteSeparator();
191189
WriteKey("value");
192-
json_stream_->Writef("\"%u\"", const_.f32_bits);
190+
if (const_.is_expected_nan) {
191+
if (const_.expected == ExpectedNan::Arithmetic) {
192+
WriteString("nan:arithmetic");
193+
} else {
194+
WriteString("nan:canonical");
195+
}
196+
} else {
197+
json_stream_->Writef("\"%u\"", const_.f32_bits);
198+
}
193199
break;
194200
}
195201

@@ -198,7 +204,15 @@ void BinaryWriterSpec::WriteConst(const Const& const_) {
198204
WriteString("f64");
199205
WriteSeparator();
200206
WriteKey("value");
201-
json_stream_->Writef("\"%" PRIu64 "\"", const_.f64_bits);
207+
if (const_.is_expected_nan) {
208+
if (const_.expected == ExpectedNan::Arithmetic) {
209+
WriteString("nan:arithmetic");
210+
} else {
211+
WriteString("nan:canonical");
212+
}
213+
} else {
214+
json_stream_->Writef("\"%" PRIu64 "\"", const_.f64_bits);
215+
}
202216
break;
203217
}
204218

@@ -490,30 +504,6 @@ void BinaryWriterSpec::WriteCommands() {
490504
break;
491505
}
492506

493-
case CommandType::AssertReturnCanonicalNan: {
494-
auto* assert_return_canonical_nan_command =
495-
cast<AssertReturnCanonicalNanCommand>(command);
496-
WriteLocation(assert_return_canonical_nan_command->action->loc);
497-
WriteSeparator();
498-
WriteAction(*assert_return_canonical_nan_command->action);
499-
WriteSeparator();
500-
WriteKey("expected");
501-
WriteActionResultType(*assert_return_canonical_nan_command->action);
502-
break;
503-
}
504-
505-
case CommandType::AssertReturnArithmeticNan: {
506-
auto* assert_return_arithmetic_nan_command =
507-
cast<AssertReturnArithmeticNanCommand>(command);
508-
WriteLocation(assert_return_arithmetic_nan_command->action->loc);
509-
WriteSeparator();
510-
WriteAction(*assert_return_arithmetic_nan_command->action);
511-
WriteSeparator();
512-
WriteKey("expected");
513-
WriteActionResultType(*assert_return_arithmetic_nan_command->action);
514-
break;
515-
}
516-
517507
case CommandType::AssertTrap: {
518508
auto* assert_trap_command = cast<AssertTrapCommand>(command);
519509
WriteLocation(assert_trap_command->action->loc);

src/common.h

+6
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,12 @@ enum class Type : int32_t {
229229
};
230230
typedef std::vector<Type> TypeVector;
231231

232+
// Used in test asserts for special expected values "nan:canonical" and "nan:arithmetic"
233+
enum class ExpectedNan {
234+
Canonical,
235+
Arithmetic,
236+
};
237+
232238
// Matches binary format, do not change.
233239
enum SegmentFlags : uint8_t {
234240
SegFlagsNone = 0,

src/interp/binary-reader-interp.cc

+5-11
Original file line numberDiff line numberDiff line change
@@ -1130,11 +1130,6 @@ wabt::Result BinaryReaderInterp::OnElemSegmentElemExprCount(Index index,
11301130
if (segment_flags_ & SegPassive) {
11311131
elem_segment_info_ = nullptr;
11321132
} else {
1133-
// An active segment still is present in the segment index space, but
1134-
// cannot be used with `table.init` (it's as if it has already been
1135-
// dropped).
1136-
elem_segment_->dropped = true;
1137-
11381133
assert(segment_table_index_ != kInvalidIndex);
11391134
Table* table = GetTableByModuleIndex(segment_table_index_);
11401135
module_->active_elem_segments_.emplace_back(table, table_offset_);
@@ -1146,7 +1141,11 @@ wabt::Result BinaryReaderInterp::OnElemSegmentElemExprCount(Index index,
11461141
wabt::Result BinaryReaderInterp::OnElemSegmentElemExpr_RefNull(
11471142
Index segment_index) {
11481143
assert(segment_flags_ & SegUseElemExprs);
1149-
elem_segment_->elems.push_back({RefType::Null, kInvalidIndex});
1144+
if (segment_flags_ & SegPassive) {
1145+
elem_segment_->elems.push_back({RefType::Null, kInvalidIndex});
1146+
} else {
1147+
elem_segment_info_->src.push_back({RefType::Null, kInvalidIndex});
1148+
}
11501149
return wabt::Result::Ok;
11511150
}
11521151

@@ -1207,11 +1206,6 @@ wabt::Result BinaryReaderInterp::OnDataSegmentData(Index index,
12071206
return wabt::Result::Error;
12081207
}
12091208

1210-
// An active segment still is present in the segment index space, but
1211-
// cannot be used with `memory.init` (it's as if it has already been
1212-
// dropped).
1213-
segment->dropped = true;
1214-
12151209
assert(module_->memory_index != kInvalidIndex);
12161210
Memory* memory = env_->GetMemory(module_->memory_index);
12171211
Address address = init_expr_value_.value.i32;

src/interp/interp.cc

+54-48
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,11 @@ Result Thread::AtomicRmwCmpxchg(const uint8_t** pc) {
10181018
return Push<ResultValueType>(static_cast<ExtendedType>(read));
10191019
}
10201020

1021-
bool ClampToBounds(uint32_t start, uint32_t* length, uint32_t max) {
1021+
// This function is used to clanp the bound for table.fill operations. This
1022+
// can be removed once the behaviour of table.fill is updated to match the
1023+
// new bulk meory semantics, which is to trap early on OOB.
1024+
// See: https://github.com/webassembly/bulk-memory-operations/issues/111
1025+
static bool ClampToBounds(uint32_t start, uint32_t* length, uint32_t max) {
10221026
if (start > max) {
10231027
*length = 0;
10241028
return false;
@@ -1031,6 +1035,17 @@ bool ClampToBounds(uint32_t start, uint32_t* length, uint32_t max) {
10311035
return true;
10321036
}
10331037

1038+
static bool CheckBounds(uint32_t start, uint32_t length, uint32_t max) {
1039+
if (start > max) {
1040+
return false;
1041+
}
1042+
uint32_t avail = max - start;
1043+
if (length > avail) {
1044+
return false;
1045+
}
1046+
return true;
1047+
}
1048+
10341049
Result Thread::MemoryInit(const uint8_t** pc) {
10351050
Memory* memory = ReadMemory(pc);
10361051
DataSegment* segment = ReadDataSegment(pc);
@@ -1039,22 +1054,20 @@ Result Thread::MemoryInit(const uint8_t** pc) {
10391054
uint32_t size = Pop<uint32_t>();
10401055
uint32_t src = Pop<uint32_t>();
10411056
uint32_t dst = Pop<uint32_t>();
1057+
bool ok = CheckBounds(dst, size, memory_size);
1058+
ok &= CheckBounds(src, size, segment_size);
1059+
if (!ok) {
1060+
TRAP_MSG(MemoryAccessOutOfBounds, "memory.init out of bounds");
1061+
}
10421062
if (size > 0) {
1043-
TRAP_IF(segment->dropped, DataSegmentDropped);
1044-
bool ok = ClampToBounds(dst, &size, memory_size);
1045-
ok &= ClampToBounds(src, &size, segment_size);
1046-
if (!ok) {
1047-
TRAP_MSG(MemoryAccessOutOfBounds, "memory.init out of bounds");
1048-
}
10491063
memcpy(memory->data.data() + dst, segment->data.data() + src, size);
10501064
}
10511065
return ResultType::Ok;
10521066
}
10531067

10541068
Result Thread::DataDrop(const uint8_t** pc) {
10551069
DataSegment* segment = ReadDataSegment(pc);
1056-
TRAP_IF(segment->dropped, DataSegmentDropped);
1057-
segment->dropped = true;
1070+
segment->data.clear();
10581071
return ResultType::Ok;
10591072
}
10601073

@@ -1064,12 +1077,12 @@ Result Thread::MemoryCopy(const uint8_t** pc) {
10641077
uint32_t size = Pop<uint32_t>();
10651078
uint32_t src = Pop<uint32_t>();
10661079
uint32_t dst = Pop<uint32_t>();
1080+
bool ok = CheckBounds(dst, size, memory_size);
1081+
ok &= CheckBounds(src, size, memory_size);
1082+
if (!ok) {
1083+
TRAP_MSG(MemoryAccessOutOfBounds, "memory.copy out of bound");
1084+
}
10671085
if (size > 0) {
1068-
bool ok = ClampToBounds(dst, &size, memory_size);
1069-
ok &= ClampToBounds(src, &size, memory_size);
1070-
if (!ok) {
1071-
TRAP_MSG(MemoryAccessOutOfBounds, "memory.copy out of bound");
1072-
}
10731086
char* data = memory->data.data();
10741087
memmove(data + dst, data + src, size);
10751088
}
@@ -1082,11 +1095,10 @@ Result Thread::MemoryFill(const uint8_t** pc) {
10821095
uint32_t size = Pop<uint32_t>();
10831096
uint8_t value = static_cast<uint8_t>(Pop<uint32_t>());
10841097
uint32_t dst = Pop<uint32_t>();
1098+
if (!CheckBounds(dst, size, memory_size)) {
1099+
TRAP_MSG(MemoryAccessOutOfBounds, "memory.fill out of bounds");
1100+
}
10851101
if (size > 0) {
1086-
bool ok = ClampToBounds(dst, &size, memory_size);
1087-
if (!ok) {
1088-
TRAP_MSG(MemoryAccessOutOfBounds, "memory.fill out of bounds");
1089-
}
10901102
memset(memory->data.data() + dst, value, size);
10911103
}
10921104
return ResultType::Ok;
@@ -1099,13 +1111,12 @@ Result Thread::TableInit(const uint8_t** pc) {
10991111
uint32_t size = Pop<uint32_t>();
11001112
uint32_t src = Pop<uint32_t>();
11011113
uint32_t dst = Pop<uint32_t>();
1114+
bool ok = CheckBounds(dst, size, table->size());
1115+
ok &= CheckBounds(src, size, segment_size);
1116+
if (!ok) {
1117+
TRAP_MSG(TableAccessOutOfBounds, "table.init out of bounds");
1118+
}
11021119
if (size > 0) {
1103-
TRAP_IF(segment->dropped, ElemSegmentDropped);
1104-
bool ok = ClampToBounds(dst, &size, table->size());
1105-
ok &= ClampToBounds(src, &size, segment_size);
1106-
if (!ok) {
1107-
TRAP_MSG(TableAccessOutOfBounds, "table.init out of bounds");
1108-
}
11091120
memcpy(table->entries.data() + dst, segment->elems.data() + src,
11101121
size * sizeof(table->entries[0]));
11111122
}
@@ -1144,14 +1155,13 @@ Result Thread::TableFill(const uint8_t** pc) {
11441155
for (uint32_t i = 0; i < size; i++) {
11451156
table->entries[dst+i] = value;
11461157
}
1147-
TRAP_IF(!ok, MemoryAccessOutOfBounds);
1158+
TRAP_IF(!ok, TableAccessOutOfBounds);
11481159
return ResultType::Ok;
11491160
}
11501161

11511162
Result Thread::ElemDrop(const uint8_t** pc) {
11521163
ElemSegment* segment = ReadElemSegment(pc);
1153-
TRAP_IF(segment->dropped, ElemSegmentDropped);
1154-
segment->dropped = true;
1164+
segment->elems.clear();
11551165
return ResultType::Ok;
11561166
}
11571167

@@ -1162,12 +1172,12 @@ Result Thread::TableCopy(const uint8_t** pc) {
11621172
uint32_t size = Pop<uint32_t>();
11631173
uint32_t src = Pop<uint32_t>();
11641174
uint32_t dst = Pop<uint32_t>();
1175+
bool ok = CheckBounds(dst, size, dst_table->size());
1176+
ok &= CheckBounds(src, size, dst_table->size());
1177+
if (!ok) {
1178+
TRAP_MSG(TableAccessOutOfBounds, "table.copy out of bounds");
1179+
}
11651180
if (size > 0) {
1166-
bool ok = ClampToBounds(dst, &size, dst_table->size());
1167-
ok &= ClampToBounds(src, &size, dst_table->size());
1168-
if (!ok) {
1169-
TRAP_MSG(TableAccessOutOfBounds, "table.copy out of bounds");
1170-
}
11711181
Ref* data_src = src_table->entries.data();
11721182
Ref* data_dst = dst_table->entries.data();
11731183
memmove(data_dst + dst, data_src + src, size * sizeof(Ref));
@@ -3755,40 +3765,36 @@ Result Executor::InitializeSegments(DefinedModule* module) {
37553765
uint32_t table_size = info.table->size();
37563766
uint32_t segment_size = info.src.size();
37573767
uint32_t copy_size = segment_size;
3758-
bool ok = ClampToBounds(info.dst, &copy_size, table_size);
3759-
3760-
if (pass == Init && copy_size > 0) {
3761-
std::copy(info.src.begin(), info.src.begin() + copy_size,
3762-
info.table->entries.begin() + info.dst);
3763-
}
3764-
3765-
if (!ok) {
3768+
if (!CheckBounds(info.dst, copy_size, table_size)) {
37663769
TRAP_MSG(TableAccessOutOfBounds,
37673770
"elem segment is out of bounds: [%u, %" PRIu64
37683771
") >= max value %u",
37693772
info.dst, static_cast<uint64_t>(info.dst) + segment_size,
37703773
table_size);
37713774
}
3775+
3776+
if (pass == Init && copy_size > 0) {
3777+
std::copy(info.src.begin(), info.src.begin() + copy_size,
3778+
info.table->entries.begin() + info.dst);
3779+
}
37723780
}
37733781

37743782
for (const DataSegmentInfo& info : module->active_data_segments_) {
37753783
uint32_t memory_size = info.memory->data.size();
37763784
uint32_t segment_size = info.data.size();
37773785
uint32_t copy_size = segment_size;
3778-
bool ok = ClampToBounds(info.dst, &copy_size, memory_size);
3779-
3780-
if (pass == Init && copy_size > 0) {
3781-
std::copy(info.data.begin(), info.data.begin() + copy_size,
3782-
info.memory->data.begin() + info.dst);
3783-
}
3784-
3785-
if (!ok) {
3786+
if (!CheckBounds(info.dst, copy_size, memory_size)) {
37863787
TRAP_MSG(MemoryAccessOutOfBounds,
37873788
"data segment is out of bounds: [%u, %" PRIu64
37883789
") >= max value %u",
37893790
info.dst, static_cast<uint64_t>(info.dst) + segment_size,
37903791
memory_size);
37913792
}
3793+
3794+
if (pass == Init && copy_size > 0) {
3795+
std::copy(info.data.begin(), info.data.begin() + copy_size,
3796+
info.memory->data.begin() + info.dst);
3797+
}
37923798
}
37933799
}
37943800

src/interp/interp.h

+1-9
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,6 @@ namespace interp {
6666
V(TrapHostResultTypeMismatch, "host result type mismatch") \
6767
/* we called an import function, but it didn't complete succesfully */ \
6868
V(TrapHostTrapped, "host function trapped") \
69-
/* the data segment has been dropped. */ \
70-
V(TrapDataSegmentDropped, "data segment dropped") \
71-
/* the element segment has been dropped. */ \
72-
V(TrapElemSegmentDropped, "element segment dropped") \
7369
/* table access is out of bounds */ \
7470
V(TrapTableAccessOutOfBounds, "out of bounds table access") \
7571
/* we attempted to call a function with the an argument list that doesn't \
@@ -150,14 +146,12 @@ struct DataSegment {
150146
DataSegment() = default;
151147

152148
std::vector<char> data;
153-
bool dropped = false;
154149
};
155150

156151
struct ElemSegment {
157152
ElemSegment() = default;
158153

159154
std::vector<Ref> elems;
160-
bool dropped = false;
161155
};
162156

163157
struct ElemSegmentInfo {
@@ -208,7 +202,7 @@ union Value {
208202
};
209203

210204
struct TypedValue {
211-
TypedValue() {}
205+
TypedValue() = default;
212206
explicit TypedValue(Type type) : type(type) {}
213207
TypedValue(Type basetype, const Value& value) : type(basetype), value(value) {
214208
UpdateType();
@@ -780,8 +774,6 @@ std::string TypedValueToString(const TypedValue&);
780774
std::string ResultToString(Result);
781775
const char* ResultTypeToString(ResultType);
782776

783-
bool ClampToBounds(uint32_t start, uint32_t* length, uint32_t max);
784-
785777
void WriteTypedValue(Stream* stream, const TypedValue&);
786778
void WriteTypedValues(Stream* stream, const TypedValues&);
787779
void WriteResult(Stream* stream, const char* desc, Result);

0 commit comments

Comments
 (0)