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