@@ -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,14 @@ 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+ return length <= avail;
1044+ }
1045+
10341046Result Thread::MemoryInit (const uint8_t ** pc) {
10351047 Memory* memory = ReadMemory (pc);
10361048 DataSegment* segment = ReadDataSegment (pc);
@@ -1039,22 +1051,20 @@ Result Thread::MemoryInit(const uint8_t** pc) {
10391051 uint32_t size = Pop<uint32_t >();
10401052 uint32_t src = Pop<uint32_t >();
10411053 uint32_t dst = Pop<uint32_t >();
1054+ bool ok = CheckBounds (dst, size, memory_size);
1055+ ok &= CheckBounds (src, size, segment_size);
1056+ if (!ok) {
1057+ TRAP_MSG (MemoryAccessOutOfBounds, " memory.init out of bounds" );
1058+ }
10421059 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- }
10491060 memcpy (memory->data .data () + dst, segment->data .data () + src, size);
10501061 }
10511062 return ResultType::Ok;
10521063}
10531064
10541065Result Thread::DataDrop (const uint8_t ** pc) {
10551066 DataSegment* segment = ReadDataSegment (pc);
1056- TRAP_IF (segment->dropped , DataSegmentDropped);
1057- segment->dropped = true ;
1067+ segment->data .clear ();
10581068 return ResultType::Ok;
10591069}
10601070
@@ -1064,12 +1074,12 @@ Result Thread::MemoryCopy(const uint8_t** pc) {
10641074 uint32_t size = Pop<uint32_t >();
10651075 uint32_t src = Pop<uint32_t >();
10661076 uint32_t dst = Pop<uint32_t >();
1077+ bool ok = CheckBounds (dst, size, memory_size);
1078+ ok &= CheckBounds (src, size, memory_size);
1079+ if (!ok) {
1080+ TRAP_MSG (MemoryAccessOutOfBounds, " memory.copy out of bound" );
1081+ }
10671082 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- }
10731083 char * data = memory->data .data ();
10741084 memmove (data + dst, data + src, size);
10751085 }
@@ -1082,11 +1092,10 @@ Result Thread::MemoryFill(const uint8_t** pc) {
10821092 uint32_t size = Pop<uint32_t >();
10831093 uint8_t value = static_cast <uint8_t >(Pop<uint32_t >());
10841094 uint32_t dst = Pop<uint32_t >();
1095+ if (!CheckBounds (dst, size, memory_size)) {
1096+ TRAP_MSG (MemoryAccessOutOfBounds, " memory.fill out of bounds" );
1097+ }
10851098 if (size > 0 ) {
1086- bool ok = ClampToBounds (dst, &size, memory_size);
1087- if (!ok) {
1088- TRAP_MSG (MemoryAccessOutOfBounds, " memory.fill out of bounds" );
1089- }
10901099 memset (memory->data .data () + dst, value, size);
10911100 }
10921101 return ResultType::Ok;
@@ -1099,13 +1108,12 @@ Result Thread::TableInit(const uint8_t** pc) {
10991108 uint32_t size = Pop<uint32_t >();
11001109 uint32_t src = Pop<uint32_t >();
11011110 uint32_t dst = Pop<uint32_t >();
1111+ bool ok = CheckBounds (dst, size, table->size ());
1112+ ok &= CheckBounds (src, size, segment_size);
1113+ if (!ok) {
1114+ TRAP_MSG (TableAccessOutOfBounds, " table.init out of bounds" );
1115+ }
11021116 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- }
11091117 memcpy (table->entries .data () + dst, segment->elems .data () + src,
11101118 size * sizeof (table->entries [0 ]));
11111119 }
@@ -1144,14 +1152,13 @@ Result Thread::TableFill(const uint8_t** pc) {
11441152 for (uint32_t i = 0 ; i < size; i++) {
11451153 table->entries [dst+i] = value;
11461154 }
1147- TRAP_IF (!ok, MemoryAccessOutOfBounds );
1155+ TRAP_IF (!ok, TableAccessOutOfBounds );
11481156 return ResultType::Ok;
11491157}
11501158
11511159Result Thread::ElemDrop (const uint8_t ** pc) {
11521160 ElemSegment* segment = ReadElemSegment (pc);
1153- TRAP_IF (segment->dropped , ElemSegmentDropped);
1154- segment->dropped = true ;
1161+ segment->elems .clear ();
11551162 return ResultType::Ok;
11561163}
11571164
@@ -1162,12 +1169,12 @@ Result Thread::TableCopy(const uint8_t** pc) {
11621169 uint32_t size = Pop<uint32_t >();
11631170 uint32_t src = Pop<uint32_t >();
11641171 uint32_t dst = Pop<uint32_t >();
1172+ bool ok = CheckBounds (dst, size, dst_table->size ());
1173+ ok &= CheckBounds (src, size, dst_table->size ());
1174+ if (!ok) {
1175+ TRAP_MSG (TableAccessOutOfBounds, " table.copy out of bounds" );
1176+ }
11651177 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- }
11711178 Ref* data_src = src_table->entries .data ();
11721179 Ref* data_dst = dst_table->entries .data ();
11731180 memmove (data_dst + dst, data_src + src, size * sizeof (Ref));
@@ -3755,40 +3762,36 @@ Result Executor::InitializeSegments(DefinedModule* module) {
37553762 uint32_t table_size = info.table ->size ();
37563763 uint32_t segment_size = info.src .size ();
37573764 uint32_t copy_size = segment_size;
3758- bool ok = ClampToBounds (info.dst , ©_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) {
3765+ if (!CheckBounds (info.dst , copy_size, table_size)) {
37663766 TRAP_MSG (TableAccessOutOfBounds,
37673767 " elem segment is out of bounds: [%u, %" PRIu64
37683768 " ) >= max value %u" ,
37693769 info.dst , static_cast <uint64_t >(info.dst ) + segment_size,
37703770 table_size);
37713771 }
3772+
3773+ if (pass == Init && copy_size > 0 ) {
3774+ std::copy (info.src .begin (), info.src .begin () + copy_size,
3775+ info.table ->entries .begin () + info.dst );
3776+ }
37723777 }
37733778
37743779 for (const DataSegmentInfo& info : module ->active_data_segments_ ) {
37753780 uint32_t memory_size = info.memory ->data .size ();
37763781 uint32_t segment_size = info.data .size ();
37773782 uint32_t copy_size = segment_size;
3778- bool ok = ClampToBounds (info.dst , ©_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) {
3783+ if (!CheckBounds (info.dst , copy_size, memory_size)) {
37863784 TRAP_MSG (MemoryAccessOutOfBounds,
37873785 " data segment is out of bounds: [%u, %" PRIu64
37883786 " ) >= max value %u" ,
37893787 info.dst , static_cast <uint64_t >(info.dst ) + segment_size,
37903788 memory_size);
37913789 }
3790+
3791+ if (pass == Init && copy_size > 0 ) {
3792+ std::copy (info.data .begin (), info.data .begin () + copy_size,
3793+ info.memory ->data .begin () + info.dst );
3794+ }
37923795 }
37933796 }
37943797
0 commit comments