1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 *
4 * Copyright 2021 Mozilla Foundation
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include "wasm/WasmBinary.h"
20
21 #include "js/Printf.h"
22 #include "wasm/WasmValidate.h"
23
24 using namespace js;
25 using namespace js::wasm;
26
27 // Decoder implementation.
28
failf(const char * msg,...)29 bool Decoder::failf(const char* msg, ...) {
30 va_list ap;
31 va_start(ap, msg);
32 UniqueChars str(JS_vsmprintf(msg, ap));
33 va_end(ap);
34 if (!str) {
35 return false;
36 }
37
38 return fail(str.get());
39 }
40
warnf(const char * msg,...)41 void Decoder::warnf(const char* msg, ...) {
42 if (!warnings_) {
43 return;
44 }
45
46 va_list ap;
47 va_start(ap, msg);
48 UniqueChars str(JS_vsmprintf(msg, ap));
49 va_end(ap);
50 if (!str) {
51 return;
52 }
53
54 (void)warnings_->append(std::move(str));
55 }
56
fail(size_t errorOffset,const char * msg)57 bool Decoder::fail(size_t errorOffset, const char* msg) {
58 MOZ_ASSERT(error_);
59 UniqueChars strWithOffset(JS_smprintf("at offset %zu: %s", errorOffset, msg));
60 if (!strWithOffset) {
61 return false;
62 }
63
64 *error_ = std::move(strWithOffset);
65 return false;
66 }
67
readSectionHeader(uint8_t * id,SectionRange * range)68 bool Decoder::readSectionHeader(uint8_t* id, SectionRange* range) {
69 if (!readFixedU8(id)) {
70 return false;
71 }
72
73 uint32_t size;
74 if (!readVarU32(&size)) {
75 return false;
76 }
77
78 range->start = currentOffset();
79 range->size = size;
80 return true;
81 }
82
startSection(SectionId id,ModuleEnvironment * env,MaybeSectionRange * range,const char * sectionName)83 bool Decoder::startSection(SectionId id, ModuleEnvironment* env,
84 MaybeSectionRange* range, const char* sectionName) {
85 MOZ_ASSERT(!*range);
86
87 // Record state at beginning of section to allow rewinding to this point
88 // if, after skipping through several custom sections, we don't find the
89 // section 'id'.
90 const uint8_t* const initialCur = cur_;
91 const size_t initialCustomSectionsLength = env->customSections.length();
92
93 // Maintain a pointer to the current section that gets updated as custom
94 // sections are skipped.
95 const uint8_t* currentSectionStart = cur_;
96
97 // Only start a section with 'id', skipping any custom sections before it.
98
99 uint8_t idValue;
100 if (!readFixedU8(&idValue)) {
101 goto rewind;
102 }
103
104 while (idValue != uint8_t(id)) {
105 if (idValue != uint8_t(SectionId::Custom)) {
106 goto rewind;
107 }
108
109 // Rewind to the beginning of the current section since this is what
110 // skipCustomSection() assumes.
111 cur_ = currentSectionStart;
112 if (!skipCustomSection(env)) {
113 return false;
114 }
115
116 // Having successfully skipped a custom section, consider the next
117 // section.
118 currentSectionStart = cur_;
119 if (!readFixedU8(&idValue)) {
120 goto rewind;
121 }
122 }
123
124 // Don't check the size since the range of bytes being decoded might not
125 // contain the section body. (This is currently the case when streaming: the
126 // code section header is decoded with the module environment bytes, the
127 // body of the code section is streamed in separately.)
128
129 uint32_t size;
130 if (!readVarU32(&size)) {
131 goto fail;
132 }
133
134 range->emplace();
135 (*range)->start = currentOffset();
136 (*range)->size = size;
137 return true;
138
139 rewind:
140 cur_ = initialCur;
141 env->customSections.shrinkTo(initialCustomSectionsLength);
142 return true;
143
144 fail:
145 return failf("failed to start %s section", sectionName);
146 }
147
finishSection(const SectionRange & range,const char * sectionName)148 bool Decoder::finishSection(const SectionRange& range,
149 const char* sectionName) {
150 if (resilientMode_) {
151 return true;
152 }
153 if (range.size != currentOffset() - range.start) {
154 return failf("byte size mismatch in %s section", sectionName);
155 }
156 return true;
157 }
158
startCustomSection(const char * expected,size_t expectedLength,ModuleEnvironment * env,MaybeSectionRange * range)159 bool Decoder::startCustomSection(const char* expected, size_t expectedLength,
160 ModuleEnvironment* env,
161 MaybeSectionRange* range) {
162 // Record state at beginning of section to allow rewinding to this point
163 // if, after skipping through several custom sections, we don't find the
164 // section 'id'.
165 const uint8_t* const initialCur = cur_;
166 const size_t initialCustomSectionsLength = env->customSections.length();
167
168 while (true) {
169 // Try to start a custom section. If we can't, rewind to the beginning
170 // since we may have skipped several custom sections already looking for
171 // 'expected'.
172 if (!startSection(SectionId::Custom, env, range, "custom")) {
173 return false;
174 }
175 if (!*range) {
176 goto rewind;
177 }
178
179 if (bytesRemain() < (*range)->size) {
180 goto fail;
181 }
182
183 CustomSectionEnv sec;
184 if (!readVarU32(&sec.nameLength) || sec.nameLength > bytesRemain()) {
185 goto fail;
186 }
187
188 sec.nameOffset = currentOffset();
189 sec.payloadOffset = sec.nameOffset + sec.nameLength;
190
191 uint32_t payloadEnd = (*range)->start + (*range)->size;
192 if (sec.payloadOffset > payloadEnd) {
193 goto fail;
194 }
195
196 sec.payloadLength = payloadEnd - sec.payloadOffset;
197
198 // Now that we have a valid custom section, record its offsets in the
199 // metadata which can be queried by the user via Module.customSections.
200 // Note: after an entry is appended, it may be popped if this loop or
201 // the loop in startSection needs to rewind.
202 if (!env->customSections.append(sec)) {
203 return false;
204 }
205
206 // If this is the expected custom section, we're done.
207 if (!expected || (expectedLength == sec.nameLength &&
208 !memcmp(cur_, expected, sec.nameLength))) {
209 cur_ += sec.nameLength;
210 return true;
211 }
212
213 // Otherwise, blindly skip the custom section and keep looking.
214 skipAndFinishCustomSection(**range);
215 range->reset();
216 }
217 MOZ_CRASH("unreachable");
218
219 rewind:
220 cur_ = initialCur;
221 env->customSections.shrinkTo(initialCustomSectionsLength);
222 return true;
223
224 fail:
225 return fail("failed to start custom section");
226 }
227
finishCustomSection(const char * name,const SectionRange & range)228 void Decoder::finishCustomSection(const char* name, const SectionRange& range) {
229 MOZ_ASSERT(cur_ >= beg_);
230 MOZ_ASSERT(cur_ <= end_);
231
232 if (error_ && *error_) {
233 warnf("in the '%s' custom section: %s", name, error_->get());
234 skipAndFinishCustomSection(range);
235 return;
236 }
237
238 uint32_t actualSize = currentOffset() - range.start;
239 if (range.size != actualSize) {
240 if (actualSize < range.size) {
241 warnf("in the '%s' custom section: %" PRIu32 " unconsumed bytes", name,
242 uint32_t(range.size - actualSize));
243 } else {
244 warnf("in the '%s' custom section: %" PRIu32
245 " bytes consumed past the end",
246 name, uint32_t(actualSize - range.size));
247 }
248 skipAndFinishCustomSection(range);
249 return;
250 }
251
252 // Nothing to do! (c.f. skipAndFinishCustomSection())
253 }
254
skipAndFinishCustomSection(const SectionRange & range)255 void Decoder::skipAndFinishCustomSection(const SectionRange& range) {
256 MOZ_ASSERT(cur_ >= beg_);
257 MOZ_ASSERT(cur_ <= end_);
258 cur_ = (beg_ + (range.start - offsetInModule_)) + range.size;
259 MOZ_ASSERT(cur_ <= end_);
260 clearError();
261 }
262
skipCustomSection(ModuleEnvironment * env)263 bool Decoder::skipCustomSection(ModuleEnvironment* env) {
264 MaybeSectionRange range;
265 if (!startCustomSection(nullptr, 0, env, &range)) {
266 return false;
267 }
268 if (!range) {
269 return fail("expected custom section");
270 }
271
272 skipAndFinishCustomSection(*range);
273 return true;
274 }
275
startNameSubsection(NameType nameType,Maybe<uint32_t> * endOffset)276 bool Decoder::startNameSubsection(NameType nameType,
277 Maybe<uint32_t>* endOffset) {
278 MOZ_ASSERT(!*endOffset);
279
280 const uint8_t* const initialPosition = cur_;
281
282 uint8_t nameTypeValue;
283 if (!readFixedU8(&nameTypeValue)) {
284 goto rewind;
285 }
286
287 if (nameTypeValue != uint8_t(nameType)) {
288 goto rewind;
289 }
290
291 uint32_t payloadLength;
292 if (!readVarU32(&payloadLength) || payloadLength > bytesRemain()) {
293 return fail("bad name subsection payload length");
294 }
295
296 *endOffset = Some(currentOffset() + payloadLength);
297 return true;
298
299 rewind:
300 cur_ = initialPosition;
301 return true;
302 }
303
finishNameSubsection(uint32_t endOffset)304 bool Decoder::finishNameSubsection(uint32_t endOffset) {
305 uint32_t actual = currentOffset();
306 if (endOffset != actual) {
307 return failf("bad name subsection length (endOffset: %" PRIu32
308 ", actual: %" PRIu32 ")",
309 endOffset, actual);
310 }
311
312 return true;
313 }
314
skipNameSubsection()315 bool Decoder::skipNameSubsection() {
316 uint8_t nameTypeValue;
317 if (!readFixedU8(&nameTypeValue)) {
318 return fail("unable to read name subsection id");
319 }
320
321 switch (nameTypeValue) {
322 case uint8_t(NameType::Module):
323 case uint8_t(NameType::Function):
324 return fail("out of order name subsections");
325 default:
326 break;
327 }
328
329 uint32_t payloadLength;
330 if (!readVarU32(&payloadLength) || !readBytes(payloadLength)) {
331 return fail("bad name subsection payload length");
332 }
333
334 return true;
335 }
336