| // Copyright 2017 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "test/unittests/test-utils.h" |
| |
| #include "src/objects-inl.h" |
| |
| #include "src/wasm/module-decoder.h" |
| #include "src/wasm/streaming-decoder.h" |
| |
| #include "src/objects/descriptor-array.h" |
| #include "src/objects/dictionary.h" |
| #include "test/common/wasm/wasm-macro-gen.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace wasm { |
| |
| class MockStreamingProcessor : public StreamingProcessor { |
| public: |
| bool ProcessModuleHeader(Vector<const uint8_t> bytes, |
| uint32_t offset) override { |
| // TODO(ahaas): Share code with the module-decoder. |
| Decoder decoder(bytes.begin(), bytes.end()); |
| uint32_t magic_word = decoder.consume_u32("wasm magic"); |
| if (decoder.failed() || magic_word != kWasmMagic) { |
| ok_ = false; |
| return false; |
| } |
| uint32_t magic_version = decoder.consume_u32("wasm version"); |
| if (decoder.failed() || magic_version != kWasmVersion) { |
| ok_ = false; |
| return false; |
| } |
| return true; |
| } |
| // Process all sections but the code section. |
| bool ProcessSection(SectionCode section_code, Vector<const uint8_t> bytes, |
| uint32_t offset) override { |
| ++num_sections_; |
| return true; |
| } |
| |
| bool ProcessCodeSectionHeader(size_t num_functions, |
| uint32_t offset) override { |
| return true; |
| } |
| |
| // Process a function body. |
| bool ProcessFunctionBody(Vector<const uint8_t> bytes, |
| uint32_t offset) override { |
| ++num_functions_; |
| return true; |
| } |
| |
| void OnFinishedChunk() override {} |
| |
| // Finish the processing of the stream. |
| void OnFinishedStream(std::unique_ptr<uint8_t[]> bytes, |
| size_t length) override { |
| received_bytes_ = std::move(bytes); |
| length_ = length; |
| } |
| |
| // Report an error detected in the StreamingDecoder. |
| void OnError(DecodeResult result) override { ok_ = false; } |
| |
| void OnAbort() override {} |
| |
| size_t num_sections() const { return num_sections_; } |
| size_t num_functions() const { return num_functions_; } |
| bool ok() const { return ok_; } |
| Vector<const uint8_t> received_bytes() { |
| return Vector<const uint8_t>(received_bytes_.get(), length_); |
| } |
| |
| private: |
| size_t num_sections_ = 0; |
| size_t num_functions_ = 0; |
| bool ok_ = true; |
| std::unique_ptr<uint8_t[]> received_bytes_; |
| size_t length_; |
| }; |
| |
| class WasmStreamingDecoderTest : public ::testing::Test { |
| public: |
| void ExpectVerifies(Vector<const uint8_t> data, size_t expected_sections, |
| size_t expected_functions) { |
| for (int split = 0; split <= data.length(); ++split) { |
| // Use a unique_ptr so that the StreamingDecoder can own the processor. |
| std::unique_ptr<MockStreamingProcessor> p(new MockStreamingProcessor()); |
| MockStreamingProcessor* processor = p.get(); |
| StreamingDecoder stream(std::move(p)); |
| stream.OnBytesReceived(data.SubVector(0, split)); |
| stream.OnBytesReceived(data.SubVector(split, data.length())); |
| stream.Finish(); |
| EXPECT_TRUE(processor->ok()); |
| EXPECT_EQ(expected_sections, processor->num_sections()); |
| EXPECT_EQ(expected_functions, processor->num_functions()); |
| EXPECT_EQ(data, processor->received_bytes()); |
| } |
| } |
| |
| void ExpectFailure(Vector<const uint8_t> data) { |
| for (int split = 0; split <= data.length(); ++split) { |
| std::unique_ptr<MockStreamingProcessor> p(new MockStreamingProcessor()); |
| MockStreamingProcessor* processor = p.get(); |
| StreamingDecoder stream(std::move(p)); |
| stream.OnBytesReceived(data.SubVector(0, split)); |
| stream.OnBytesReceived(data.SubVector(split, data.length())); |
| stream.Finish(); |
| EXPECT_FALSE(processor->ok()); |
| } |
| } |
| }; |
| |
| TEST_F(WasmStreamingDecoderTest, EmptyStream) { |
| std::unique_ptr<MockStreamingProcessor> p(new MockStreamingProcessor()); |
| MockStreamingProcessor* processor = p.get(); |
| StreamingDecoder stream(std::move(p)); |
| stream.Finish(); |
| EXPECT_FALSE(processor->ok()); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, IncompleteModuleHeader) { |
| const uint8_t data[] = {U32_LE(kWasmMagic), U32_LE(kWasmVersion)}; |
| { |
| std::unique_ptr<MockStreamingProcessor> p(new MockStreamingProcessor()); |
| MockStreamingProcessor* processor = p.get(); |
| StreamingDecoder stream(std::move(p)); |
| stream.OnBytesReceived(Vector<const uint8_t>(data, 1)); |
| stream.Finish(); |
| EXPECT_FALSE(processor->ok()); |
| } |
| for (int length = 1; length < static_cast<int>(arraysize(data)); ++length) { |
| ExpectFailure(Vector<const uint8_t>(data, length)); |
| } |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, MagicAndVersion) { |
| const uint8_t data[] = {U32_LE(kWasmMagic), U32_LE(kWasmVersion)}; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 0, 0); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, BadMagic) { |
| for (uint32_t x = 1; x; x <<= 1) { |
| const uint8_t data[] = {U32_LE(kWasmMagic ^ x), U32_LE(kWasmVersion)}; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, BadVersion) { |
| for (uint32_t x = 1; x; x <<= 1) { |
| const uint8_t data[] = {U32_LE(kWasmMagic), U32_LE(kWasmVersion ^ x)}; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, OneSection) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| 0x1, // Section ID |
| 0x6, // Section Length |
| 0x0, // Payload |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0 // 6 |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 1, 0); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, OneSection_b) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| 0x1, // Section ID |
| 0x86, // Section Length = 6 (LEB) |
| 0x0, // -- |
| 0x0, // Payload |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0 // 6 |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 1, 0); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, OneShortSection) { |
| // Short section means that section length + payload is less than 5 bytes, |
| // which is the maximum size of the length field. |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| 0x1, // Section ID |
| 0x2, // Section Length |
| 0x0, // Payload |
| 0x0 // 2 |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 1, 0); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, OneShortSection_b) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| 0x1, // Section ID |
| 0x82, // Section Length = 2 (LEB) |
| 0x80, // -- |
| 0x0, // -- |
| 0x0, // Payload |
| 0x0 // 2 |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 1, 0); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, OneEmptySection) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| 0x1, // Section ID |
| 0x0 // Section Length |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 1, 0); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, OneSectionNotEnoughPayload1) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| 0x1, // Section ID |
| 0x6, // Section Length |
| 0x0, // Payload |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0 // 5 |
| }; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, OneSectionNotEnoughPayload2) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| 0x1, // Section ID |
| 0x6, // Section Length |
| 0x0 // Payload |
| }; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, OneSectionInvalidLength) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| 0x1, // Section ID |
| 0x80, // Section Length (0 in LEB) |
| 0x80, // -- |
| 0x80, // -- |
| 0x80, // -- |
| 0x80, // -- |
| }; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, TwoLongSections) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| 0x1, // Section ID |
| 0x6, // Section Length |
| 0x0, // Payload |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| 0x2, // Section ID |
| 0x7, // Section Length |
| 0x0, // Payload |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| 0x0 // 7 |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 2, 0); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, TwoShortSections) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| 0x1, // Section ID |
| 0x1, // Section Length |
| 0x0, // Payload |
| 0x2, // Section ID |
| 0x2, // Section Length |
| 0x0, // Payload |
| 0x0, // 2 |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 2, 0); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, TwoSectionsShortLong) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| 0x1, // Section ID |
| 0x1, // Section Length |
| 0x0, // Payload |
| 0x2, // Section ID |
| 0x7, // Section Length |
| 0x0, // Payload |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| 0x0 // 7 |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 2, 0); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, TwoEmptySections) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| 0x1, // Section ID |
| 0x0, // Section Length |
| 0x2, // Section ID |
| 0x0 // Section Length |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 2, 0); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, OneFunction) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| kCodeSectionCode, // Section ID |
| 0x8, // Section Length |
| 0x1, // Number of Functions |
| 0x6, // Function Length |
| 0x0, // Function |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 0, 1); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, OneShortFunction) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| kCodeSectionCode, // Section ID |
| 0x3, // Section Length |
| 0x1, // Number of Functions |
| 0x1, // Function Length |
| 0x0, // Function |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 0, 1); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, EmptyFunction) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| kCodeSectionCode, // Section ID |
| 0x2, // Section Length |
| 0x1, // Number of Functions |
| 0x0, // Function Length |
| }; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, TwoFunctions) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| kCodeSectionCode, // Section ID |
| 0x10, // Section Length |
| 0x2, // Number of Functions |
| 0x6, // Function Length |
| 0x0, // Function |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| 0x7, // Function Length |
| 0x0, // Function |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| 0x0, // 7 |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 0, 2); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, TwoFunctions_b) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| kCodeSectionCode, // Section ID |
| 0xb, // Section Length |
| 0x2, // Number of Functions |
| 0x1, // Function Length |
| 0x0, // Function |
| 0x7, // Function Length |
| 0x0, // Function |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| 0x0, // 7 |
| }; |
| ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)), 0, 2); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, CodeSectionLengthZero) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| kCodeSectionCode, // Section ID |
| 0x0, // Section Length |
| }; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, CodeSectionLengthTooHigh) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| kCodeSectionCode, // Section ID |
| 0xd, // Section Length |
| 0x2, // Number of Functions |
| 0x7, // Function Length |
| 0x0, // Function |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| 0x0, // 7 |
| 0x1, // Function Length |
| 0x0, // Function |
| }; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, CodeSectionLengthTooLow) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| kCodeSectionCode, // Section ID |
| 0x9, // Section Length |
| 0x2, // Number of Functions |
| 0x7, // Function Length |
| 0x0, // Function |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| 0x0, // 7 |
| 0x1, // Function Length |
| 0x0, // Function |
| }; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, CodeSectionLengthTooLowEndsInNumFunctions) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| kCodeSectionCode, // Section ID |
| 0x1, // Section Length |
| 0x82, // Number of Functions |
| 0x80, // -- |
| 0x00, // -- |
| 0x7, // Function Length |
| 0x0, // Function |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| 0x0, // 7 |
| 0x1, // Function Length |
| 0x0, // Function |
| }; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, CodeSectionLengthTooLowEndsInFunctionLength) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| kCodeSectionCode, // Section ID |
| 0x5, // Section Length |
| 0x82, // Number of Functions |
| 0x80, // -- |
| 0x00, // -- |
| 0x87, // Function Length |
| 0x80, // -- |
| 0x00, // -- |
| 0x0, // Function |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| 0x0, // 7 |
| 0x1, // Function Length |
| 0x0, // Function |
| }; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, NumberOfFunctionsTooHigh) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| kCodeSectionCode, // Section ID |
| 0xb, // Section Length |
| 0x4, // Number of Functions |
| 0x7, // Function Length |
| 0x0, // Function |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| 0x0, // 7 |
| 0x1, // Function Length |
| 0x0, // Function |
| }; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| |
| TEST_F(WasmStreamingDecoderTest, NumberOfFunctionsTooLow) { |
| const uint8_t data[] = { |
| U32_LE(kWasmMagic), // -- |
| U32_LE(kWasmVersion), // -- |
| kCodeSectionCode, // Section ID |
| 0xe, // Section Length |
| 0x2, // Number of Functions |
| 0x1, // Function Length |
| 0x0, // Function |
| 0x2, // Function Length |
| 0x0, // Function |
| 0x0, // 2 |
| 0x7, // Function Length |
| 0x0, // Function |
| 0x0, // 2 |
| 0x0, // 3 |
| 0x0, // 4 |
| 0x0, // 5 |
| 0x0, // 6 |
| 0x0, // 7 |
| }; |
| ExpectFailure(Vector<const uint8_t>(data, arraysize(data))); |
| } |
| } // namespace wasm |
| } // namespace internal |
| } // namespace v8 |