1 /*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9 #include <executorch/schema/extended_header.h>
10
11 #include <gtest/gtest.h>
12
13 #include <executorch/runtime/core/result.h>
14 #include <executorch/runtime/platform/runtime.h>
15
16 using namespace ::testing;
17 using executorch::runtime::Error;
18 using executorch::runtime::ExtendedHeader;
19 using executorch::runtime::Result;
20
21 class ExtendedHeaderTest : public ::testing::Test {
22 protected:
SetUp()23 void SetUp() override {
24 // Since these tests cause ET_LOG to be called, the PAL must be initialized
25 // first.
26 executorch::runtime::runtime_init();
27 }
28 };
29
30 /**
31 * An example, valid extended header.
32 *
33 * This data is intentionally fragile. If the header layout or magic changes,
34 * this test data must change too. The layout of the header is a contract, not
35 * an implementation detail.
36 */
37 // clang-format off
38 constexpr char kExampleHeaderData[] = {
39 // Magic bytes
40 'e', 'h', '0', '0',
41 // uint32_t header size (little endian)
42 0x18, 0x00, 0x00, 0x00,
43 // uint64_t program size
44 0x71, 0x61, 0x51, 0x41, 0x31, 0x21, 0x11, 0x01,
45 // uint64_t segment base offset
46 0x72, 0x62, 0x52, 0x42, 0x32, 0x22, 0x12, 0x02,
47 };
48 // clang-format on
49
50 /// The program_size field encoded in kExampleHeaderData. Each byte is unique
51 /// within the header data.
52 constexpr uint64_t kExampleProgramSize = 0x0111213141516171;
53
54 /// The segment_base_offset field encoded in kExampleHeaderData. Each byte is
55 /// unique within the header data.
56 constexpr uint64_t kExampleSegmentBaseOffset = 0x0212223242526272;
57
58 /// The offset to the header's length field, which is in the 4 bytes after the
59 /// magic.
60 constexpr size_t kHeaderLengthOffset =
61 ExtendedHeader::kHeaderOffset + ExtendedHeader::kMagicSize;
62
63 /**
64 * Returns fake serialized Program head data that contains kExampleHeaderData at
65 * the expected offset.
66 */
CreateExampleProgramHead()67 std::vector<uint8_t> CreateExampleProgramHead() {
68 // Allocate memory representing the head of the serialized Program.
69 std::vector<uint8_t> ret(ExtendedHeader::kNumHeadBytes);
70 // Write non-zeros into it to make it more obvious if we read outside the
71 // header.
72 memset(ret.data(), 0x55, ret.size());
73 // Copy the example header into the right offset.
74 memcpy(
75 ret.data() + ExtendedHeader::kHeaderOffset,
76 kExampleHeaderData,
77 sizeof(kExampleHeaderData));
78 return ret;
79 }
80
TEST_F(ExtendedHeaderTest,ValidHeaderParsesCorrectly)81 TEST_F(ExtendedHeaderTest, ValidHeaderParsesCorrectly) {
82 std::vector<uint8_t> program = CreateExampleProgramHead();
83
84 Result<ExtendedHeader> header =
85 ExtendedHeader::Parse(program.data(), program.size());
86
87 // The header should be present.
88 ASSERT_EQ(header.error(), Error::Ok);
89
90 // Since each byte of these fields is unique, success demonstrates that the
91 // endian-to-int conversion is correct and looks at the expected bytes of the
92 // header.
93 EXPECT_EQ(header->program_size, kExampleProgramSize);
94 EXPECT_EQ(header->segment_base_offset, kExampleSegmentBaseOffset);
95 }
96
TEST_F(ExtendedHeaderTest,ShortDataFails)97 TEST_F(ExtendedHeaderTest, ShortDataFails) {
98 std::vector<uint8_t> program = CreateExampleProgramHead();
99
100 // Try parsing a smaller-than-required part of the data.
101 ASSERT_GE(program.size(), ExtendedHeader::kNumHeadBytes);
102 Result<ExtendedHeader> header =
103 ExtendedHeader::Parse(program.data(), ExtendedHeader::kNumHeadBytes - 1);
104
105 // Should have been rejected.
106 EXPECT_EQ(header.error(), Error::InvalidArgument);
107 }
108
TEST_F(ExtendedHeaderTest,MissingHeaderNotFound)109 TEST_F(ExtendedHeaderTest, MissingHeaderNotFound) {
110 // Program head data without the extended header magic bytes.
111 std::vector<uint8_t> program(ExtendedHeader::kNumHeadBytes);
112 memset(program.data(), 0x55, program.size());
113
114 // The header should not be found.
115 Result<ExtendedHeader> header =
116 ExtendedHeader::Parse(program.data(), program.size());
117 EXPECT_EQ(header.error(), Error::NotFound);
118 }
119
TEST_F(ExtendedHeaderTest,BadMagicTreatedAsMissing)120 TEST_F(ExtendedHeaderTest, BadMagicTreatedAsMissing) {
121 // Get a valid header.
122 std::vector<uint8_t> program = CreateExampleProgramHead();
123
124 // Should be present.
125 {
126 Result<ExtendedHeader> header =
127 ExtendedHeader::Parse(program.data(), program.size());
128 ASSERT_EQ(header.error(), Error::Ok);
129 }
130
131 // Change a character in the magic.
132 program[ExtendedHeader::kHeaderOffset] = 'x';
133
134 // No longer present.
135 {
136 Result<ExtendedHeader> header =
137 ExtendedHeader::Parse(program.data(), program.size());
138 EXPECT_EQ(header.error(), Error::NotFound);
139 }
140 }
141
TEST_F(ExtendedHeaderTest,ShorterHeaderLengthFails)142 TEST_F(ExtendedHeaderTest, ShorterHeaderLengthFails) {
143 // Get a valid header.
144 std::vector<uint8_t> program = CreateExampleProgramHead();
145
146 // Should be present.
147 {
148 Result<ExtendedHeader> header =
149 ExtendedHeader::Parse(program.data(), program.size());
150 ASSERT_EQ(header.error(), Error::Ok);
151 }
152
153 // Make the header length smaller.
154 // First demonstrate that we're looking in the right place.
155 EXPECT_EQ(program[kHeaderLengthOffset], 0x18);
156 program[kHeaderLengthOffset] = 0x10;
157
158 // Program now considered invalid.
159 {
160 Result<ExtendedHeader> header =
161 ExtendedHeader::Parse(program.data(), program.size());
162 EXPECT_EQ(header.error(), Error::InvalidProgram);
163 }
164 }
165
TEST_F(ExtendedHeaderTest,LongerHeaderLengthSucceeds)166 TEST_F(ExtendedHeaderTest, LongerHeaderLengthSucceeds) {
167 // Get a valid header.
168 std::vector<uint8_t> program = CreateExampleProgramHead();
169
170 // Make the header length larger.
171 // First demonstrate that we're looking in the right place.
172 EXPECT_EQ(program[kHeaderLengthOffset], 0x18);
173 program[kHeaderLengthOffset] = 0x20;
174
175 // Should still be present and contain the expected values.
176 {
177 Result<ExtendedHeader> header =
178 ExtendedHeader::Parse(program.data(), program.size());
179 ASSERT_EQ(header.error(), Error::Ok);
180 EXPECT_EQ(header->program_size, kExampleProgramSize);
181 EXPECT_EQ(header->segment_base_offset, kExampleSegmentBaseOffset);
182 }
183 }
184