Commit cb62c6ed authored by Ben Smith's avatar Ben Smith Committed by Commit Bot

[wasm] Parse DataCount section for bulk-memory

The bulk-memory proposal adds a new DataCount section that declares the
number of data segments that are expected to be seen in the Data
section. This is similar to the way the number of functions is split
between the Function and Code sections.

The DataCount section occurs before the Code section, so we can do
single-pass validation of the new `memory.init` and `memory.drop`
instructions, which have data segment indices as immediates.

Bug: v8:7747
Change-Id: Ibc5a7ee9336dbc5d0fd667572c42cb065c048e00
Reviewed-on: https://chromium-review.googlesource.com/c/1352792
Commit-Queue: Ben Smith <binji@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57951}
parent cac6b037
...@@ -83,6 +83,8 @@ const char* SectionName(SectionCode code) { ...@@ -83,6 +83,8 @@ const char* SectionName(SectionCode code) {
return "Data"; return "Data";
case kExceptionSectionCode: case kExceptionSectionCode:
return "Exception"; return "Exception";
case kDataCountSectionCode:
return "DataCount";
case kNameSectionCode: case kNameSectionCode:
return kNameString; return kNameString;
case kSourceMappingURLSectionCode: case kSourceMappingURLSectionCode:
...@@ -331,6 +333,40 @@ class ModuleDecoderImpl : public Decoder { ...@@ -331,6 +333,40 @@ class ModuleDecoderImpl : public Decoder {
#undef BYTES #undef BYTES
} }
bool CheckSectionOrder(SectionCode section_code,
SectionCode prev_section_code,
SectionCode next_section_code) {
if (next_ordered_section_ > next_section_code) {
errorf(pc(), "The %s section must appear before the %s section",
SectionName(section_code), SectionName(next_section_code));
return false;
}
if (next_ordered_section_ <= prev_section_code) {
next_ordered_section_ = prev_section_code + 1;
}
return true;
}
bool CheckUnorderedSection(SectionCode section_code) {
if (has_seen_unordered_section(section_code)) {
errorf(pc(), "Multiple %s sections not allowed",
SectionName(section_code));
return false;
}
set_seen_unordered_section(section_code);
switch (section_code) {
case kDataCountSectionCode:
return CheckSectionOrder(section_code, kElementSectionCode,
kCodeSectionCode);
case kExceptionSectionCode:
return CheckSectionOrder(section_code, kImportSectionCode,
kExportSectionCode);
default:
UNREACHABLE();
}
}
void DecodeSection(SectionCode section_code, Vector<const uint8_t> bytes, void DecodeSection(SectionCode section_code, Vector<const uint8_t> bytes,
uint32_t offset, bool verify_functions = true) { uint32_t offset, bool verify_functions = true) {
if (failed()) return; if (failed()) return;
...@@ -349,20 +385,12 @@ class ModuleDecoderImpl : public Decoder { ...@@ -349,20 +385,12 @@ class ModuleDecoderImpl : public Decoder {
switch (section_code) { switch (section_code) {
case kUnknownSectionCode: case kUnknownSectionCode:
break; break;
case kDataCountSectionCode:
case kExceptionSectionCode: case kExceptionSectionCode:
// Note: kExceptionSectionCode > kExportSectionCode, but must appear // Note: These sections have a section code that is numerically
// before the export (and code) section, as well as after the import // out-of-order with respect to their required location. So they are
// section. Hence, treat it as a special case. // treated as a special case.
if (seen_unordered_sections_ & (1 << kExceptionSectionCode)) { if (!CheckUnorderedSection(section_code)) return;
errorf(pc(), "Multiple exception sections not allowed");
return;
} else if (next_ordered_section_ > kExportSectionCode) {
errorf(pc(), "Exception section must appear before export section");
return;
} else if (next_ordered_section_ <= kImportSectionCode) {
next_ordered_section_ = kImportSectionCode + 1;
}
seen_unordered_sections_ |= 1 << kExceptionSectionCode;
break; break;
case kSourceMappingURLSectionCode: case kSourceMappingURLSectionCode:
// sourceMappingURL is a custom section and currently can occur anywhere // sourceMappingURL is a custom section and currently can occur anywhere
...@@ -420,6 +448,13 @@ class ModuleDecoderImpl : public Decoder { ...@@ -420,6 +448,13 @@ class ModuleDecoderImpl : public Decoder {
case kSourceMappingURLSectionCode: case kSourceMappingURLSectionCode:
DecodeSourceMappingURLSection(); DecodeSourceMappingURLSection();
break; break;
case kDataCountSectionCode:
if (enabled_features_.bulk_memory) {
DecodeDataCountSection();
} else {
errorf(pc(), "unexpected section: %s", SectionName(section_code));
}
break;
case kExceptionSectionCode: case kExceptionSectionCode:
if (enabled_features_.eh) { if (enabled_features_.eh) {
DecodeExceptionSection(); DecodeExceptionSection();
...@@ -833,9 +868,21 @@ class ModuleDecoderImpl : public Decoder { ...@@ -833,9 +868,21 @@ class ModuleDecoderImpl : public Decoder {
} }
} }
bool CheckDataSegmentsCount(uint32_t data_segments_count) {
if (has_seen_unordered_section(kDataCountSectionCode) &&
data_segments_count != num_declared_data_segments_) {
errorf(pc(), "data segments count %u mismatch (%u expected)",
data_segments_count, num_declared_data_segments_);
return false;
}
return true;
}
void DecodeDataSection() { void DecodeDataSection() {
uint32_t data_segments_count = uint32_t data_segments_count =
consume_count("data segments count", kV8MaxWasmDataSegments); consume_count("data segments count", kV8MaxWasmDataSegments);
if (!CheckDataSegmentsCount(data_segments_count)) return;
module_->data_segments.reserve(data_segments_count); module_->data_segments.reserve(data_segments_count);
for (uint32_t i = 0; ok() && i < data_segments_count; ++i) { for (uint32_t i = 0; ok() && i < data_segments_count; ++i) {
const byte* pos = pc(); const byte* pos = pc();
...@@ -879,8 +926,8 @@ class ModuleDecoderImpl : public Decoder { ...@@ -879,8 +926,8 @@ class ModuleDecoderImpl : public Decoder {
void DecodeNameSection() { void DecodeNameSection() {
// TODO(titzer): find a way to report name errors as warnings. // TODO(titzer): find a way to report name errors as warnings.
// ignore all but the first occurrence of name section. // ignore all but the first occurrence of name section.
if (!(seen_unordered_sections_ & (1 << kNameSectionCode))) { if (!has_seen_unordered_section(kNameSectionCode)) {
seen_unordered_sections_ |= 1 << kNameSectionCode; set_seen_unordered_section(kNameSectionCode);
// Use an inner decoder so that errors don't fail the outer decoder. // Use an inner decoder so that errors don't fail the outer decoder.
Decoder inner(start_, pc_, end_, buffer_offset_); Decoder inner(start_, pc_, end_, buffer_offset_);
// Decode all name subsections. // Decode all name subsections.
...@@ -910,16 +957,21 @@ class ModuleDecoderImpl : public Decoder { ...@@ -910,16 +957,21 @@ class ModuleDecoderImpl : public Decoder {
Decoder inner(start_, pc_, end_, buffer_offset_); Decoder inner(start_, pc_, end_, buffer_offset_);
WireBytesRef url = wasm::consume_string(inner, true, "module name"); WireBytesRef url = wasm::consume_string(inner, true, "module name");
if (inner.ok() && if (inner.ok() &&
!(seen_unordered_sections_ & (1 << kSourceMappingURLSectionCode))) { !has_seen_unordered_section(kSourceMappingURLSectionCode)) {
const byte* url_start = const byte* url_start =
inner.start() + inner.GetBufferRelativeOffset(url.offset()); inner.start() + inner.GetBufferRelativeOffset(url.offset());
module_->source_map_url.assign(reinterpret_cast<const char*>(url_start), module_->source_map_url.assign(reinterpret_cast<const char*>(url_start),
url.length()); url.length());
seen_unordered_sections_ |= 1 << kSourceMappingURLSectionCode; set_seen_unordered_section(kSourceMappingURLSectionCode);
} }
consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
} }
void DecodeDataCountSection() {
num_declared_data_segments_ =
consume_count("data segments count", kV8MaxWasmDataSegments);
}
void DecodeExceptionSection() { void DecodeExceptionSection() {
uint32_t exception_count = uint32_t exception_count =
consume_count("exception count", kV8MaxWasmExceptions); consume_count("exception count", kV8MaxWasmExceptions);
...@@ -933,25 +985,32 @@ class ModuleDecoderImpl : public Decoder { ...@@ -933,25 +985,32 @@ class ModuleDecoderImpl : public Decoder {
} }
} }
ModuleResult FinishDecoding(bool verify_functions = true) { bool CheckMismatchedCounts() {
if (ok()) { // The declared vs. defined function count is normally checked when
// The declared vs. defined function count is normally checked when // decoding the code section, but we have to check it here too in case the
// decoding the code section, but we have to check it here too in case the // code section is absent.
// code section is absent. if (module_->num_declared_functions != 0) {
if (module_->num_declared_functions != 0) { DCHECK_LT(module_->num_imported_functions, module_->functions.size());
DCHECK_LT(module_->num_imported_functions, module_->functions.size()); // We know that the code section has been decoded if the first
// We know that the code section has been decoded if the first // non-imported function has its code set.
// non-imported function has its code set. if (!module_->functions[module_->num_imported_functions].code.is_set()) {
if (!module_->functions[module_->num_imported_functions] errorf(pc(), "function count is %u, but code section is absent",
.code.is_set()) { module_->num_declared_functions);
errorf(pc(), "function count is %u, but code section is absent", return false;
module_->num_declared_functions);
}
} }
}
// Perform a similar check for the DataCount and Data sections, where data
// segments are declared but the Data section is absent.
if (!CheckDataSegmentsCount(
static_cast<uint32_t>(module_->data_segments.size()))) {
return false;
}
return true;
}
if (ok()) { ModuleResult FinishDecoding(bool verify_functions = true) {
CalculateGlobalOffsets(module_.get()); if (ok() && CheckMismatchedCounts()) {
} CalculateGlobalOffsets(module_.get());
} }
ModuleResult result = toResult(std::move(module_)); ModuleResult result = toResult(std::move(module_));
if (verify_functions && result.ok() && intermediate_result_.failed()) { if (verify_functions && result.ok() && intermediate_result_.failed()) {
...@@ -1061,6 +1120,16 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1061,6 +1120,16 @@ class ModuleDecoderImpl : public Decoder {
"not enough bits"); "not enough bits");
VoidResult intermediate_result_; VoidResult intermediate_result_;
ModuleOrigin origin_; ModuleOrigin origin_;
// The number of data segments as specified by the DataCount section.
uint32_t num_declared_data_segments_ = 0;
bool has_seen_unordered_section(SectionCode section_code) {
return seen_unordered_sections_ & (1 << section_code);
}
void set_seen_unordered_section(SectionCode section_code) {
seen_unordered_sections_ |= 1 << section_code;
}
uint32_t off(const byte* ptr) { uint32_t off(const byte* ptr) {
return static_cast<uint32_t>(ptr - start_) + buffer_offset_; return static_cast<uint32_t>(ptr - start_) + buffer_offset_;
......
...@@ -71,14 +71,19 @@ enum SectionCode : int8_t { ...@@ -71,14 +71,19 @@ enum SectionCode : int8_t {
kElementSectionCode = 9, // Elements section kElementSectionCode = 9, // Elements section
kCodeSectionCode = 10, // Function code kCodeSectionCode = 10, // Function code
kDataSectionCode = 11, // Data segments kDataSectionCode = 11, // Data segments
kExceptionSectionCode = 12, // Exception (aka. event) section kExceptionSectionCode = 12, // Exception section
kNameSectionCode = 13, // Name section (encoded as a string) kDataCountSectionCode = 13, // Number of data segments
kSourceMappingURLSectionCode = 14, // Source Map URL section
// The following sections are custom sections, and are identified using a
// string rather than an integer. Their enumeration values are not guaranteed
// to be consistent.
kNameSectionCode, // Name section (encoded as a string)
kSourceMappingURLSectionCode, // Source Map URL section
// Helper values // Helper values
kFirstSectionInModule = kTypeSectionCode, kFirstSectionInModule = kTypeSectionCode,
kLastKnownModuleSection = kSourceMappingURLSectionCode, kLastKnownModuleSection = kSourceMappingURLSectionCode,
kFirstUnorderedSection = kNameSectionCode, kFirstUnorderedSection = kExceptionSectionCode,
}; };
// Binary encoding of name section kinds. // Binary encoding of name section kinds.
......
...@@ -572,7 +572,8 @@ TEST_F(WasmModuleVerifyTest, ExceptionSectionAfterExport) { ...@@ -572,7 +572,8 @@ TEST_F(WasmModuleVerifyTest, ExceptionSectionAfterExport) {
WASM_FEATURE_SCOPE(eh); WASM_FEATURE_SCOPE(eh);
ModuleResult result = DecodeModule(data, data + sizeof(data)); ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(result, "Exception section must appear before export section"); EXPECT_NOT_OK(result,
"The Exception section must appear before the Export section");
} }
TEST_F(WasmModuleVerifyTest, ExceptionSectionBeforeImport) { TEST_F(WasmModuleVerifyTest, ExceptionSectionBeforeImport) {
...@@ -2270,6 +2271,99 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegment) { ...@@ -2270,6 +2271,99 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegment) {
EXPECT_OFF_END_FAILURE(data, arraysize(data) - 5); EXPECT_OFF_END_FAILURE(data, arraysize(data) - 5);
} }
TEST_F(WasmModuleVerifyTest, DataCountSectionCorrectPlacement) {
static const byte data[] = {SECTION(Element, ENTRY_COUNT(0)),
SECTION(DataCount, ENTRY_COUNT(0)),
SECTION(Code, ENTRY_COUNT(0))};
EXPECT_FAILURE(data);
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_VERIFIES(data);
}
TEST_F(WasmModuleVerifyTest, DataCountSectionAfterCode) {
static const byte data[] = {SECTION(Code, ENTRY_COUNT(0)),
SECTION(DataCount, ENTRY_COUNT(0))};
WASM_FEATURE_SCOPE(bulk_memory);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(result,
"The DataCount section must appear before the Code section");
}
TEST_F(WasmModuleVerifyTest, DataCountSectionBeforeElement) {
static const byte data[] = {SECTION(DataCount, ENTRY_COUNT(0)),
SECTION(Element, ENTRY_COUNT(0))};
WASM_FEATURE_SCOPE(bulk_memory);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(result, "unexpected section: Element");
}
TEST_F(WasmModuleVerifyTest, DataCountSectionAfterStartBeforeElement) {
STATIC_ASSERT(kStartSectionCode + 1 == kElementSectionCode);
static const byte data[] = {
// We need the start section for this test, but the start section must
// reference a valid function, which requires the type and function
// sections too.
SIGNATURES_SECTION(1, SIG_ENTRY_v_v), // Type section.
FUNCTION_SIGNATURES_SECTION(1, 0), // Function section.
SECTION(Start, U32V_1(0)), // Start section.
SECTION(DataCount, ENTRY_COUNT(0)), // DataCount section.
SECTION(Element, ENTRY_COUNT(0)) // Element section.
};
WASM_FEATURE_SCOPE(bulk_memory);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(result, "unexpected section: Element");
}
TEST_F(WasmModuleVerifyTest, MultipleDataCountSections) {
static const byte data[] = {SECTION(DataCount, ENTRY_COUNT(0)),
SECTION(DataCount, ENTRY_COUNT(0))};
WASM_FEATURE_SCOPE(bulk_memory);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(result, "Multiple DataCount sections not allowed");
}
TEST_F(WasmModuleVerifyTest, DataCountSegmentCountMatch) {
static const byte data[] = {
SECTION(Memory, ENTRY_COUNT(1), 0, 1), // Memory section.
SECTION(DataCount, ENTRY_COUNT(1)), // DataCount section.
SECTION(Data, ENTRY_COUNT(1), LINEAR_MEMORY_INDEX_0, // Data section.
WASM_INIT_EXPR_I32V_1(12), ADD_COUNT('h', 'i'))};
EXPECT_FAILURE(data);
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_VERIFIES(data);
}
TEST_F(WasmModuleVerifyTest, DataCountSegmentCount_greater) {
static const byte data[] = {
SECTION(Memory, ENTRY_COUNT(1), 0, 1), // Memory section.
SECTION(DataCount, ENTRY_COUNT(3)), // DataCount section.
SECTION(Data, ENTRY_COUNT(0))}; // Data section.
WASM_FEATURE_SCOPE(bulk_memory);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(result, "data segments count 0 mismatch (3 expected)");
}
TEST_F(WasmModuleVerifyTest, DataCountSegmentCount_less) {
static const byte data[] = {
SECTION(Memory, ENTRY_COUNT(1), 0, 1), // Memory section.
SECTION(DataCount, ENTRY_COUNT(0)), // DataCount section.
SECTION(Data, ENTRY_COUNT(1), LINEAR_MEMORY_INDEX_0, // Data section.
WASM_INIT_EXPR_I32V_1(12), ADD_COUNT('a', 'b', 'c'))};
WASM_FEATURE_SCOPE(bulk_memory);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(result, "data segments count 1 mismatch (0 expected)");
}
TEST_F(WasmModuleVerifyTest, DataCountSegmentCount_omitted) {
static const byte data[] = {SECTION(Memory, ENTRY_COUNT(1), 0, 1),
SECTION(DataCount, ENTRY_COUNT(1))};
WASM_FEATURE_SCOPE(bulk_memory);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(result, "data segments count 0 mismatch (1 expected)");
}
#undef WASM_FEATURE_SCOPE #undef WASM_FEATURE_SCOPE
#undef WASM_FEATURE_SCOPE_VAL #undef WASM_FEATURE_SCOPE_VAL
#undef EXPECT_INIT_EXPR #undef EXPECT_INIT_EXPR
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment