1/*
2 * Copyright (C) 2018 Yusuke Suzuki <[email protected]>.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WasmStreamingParser.h"
28
29#if ENABLE(WEBASSEMBLY)
30
31#include "WasmOps.h"
32#include "WasmSectionParser.h"
33#include "WasmSignatureInlines.h"
34#include <wtf/Optional.h>
35#include <wtf/UnalignedAccess.h>
36
37namespace JSC { namespace Wasm {
38
39namespace WasmStreamingParserInternal {
40static constexpr bool verbose = false;
41}
42
43#define WASM_STREAMING_PARSER_FAIL_IF_HELPER_FAILS(helper) \
44 do { \
45 auto helperResult = helper; \
46 if (UNLIKELY(!helperResult)) { \
47 m_errorMessage = helperResult.error(); \
48 return State::FatalError; \
49 } \
50 } while (0)
51
52ALWAYS_INLINE Optional<uint8_t> parseUInt7(const uint8_t* data, size_t& offset, size_t size)
53{
54 if (offset >= size)
55 return false;
56 uint8_t result = data[offset++];
57 if (result < 0x80)
58 return result;
59 return WTF::nullopt;
60}
61
62template <typename ...Args>
63NEVER_INLINE auto WARN_UNUSED_RETURN StreamingParser::fail(Args... args) -> State
64{
65 using namespace FailureHelper; // See ADL comment in namespace above.
66 m_errorMessage = makeString("WebAssembly.Module doesn't parse at byte "_s, String::number(m_offset), ": "_s, makeString(args)...);
67 dataLogLnIf(WasmStreamingParserInternal::verbose, m_errorMessage);
68 return State::FatalError;
69}
70
71StreamingParser::StreamingParser(ModuleInformation& info, StreamingParserClient& client)
72 : m_info(info)
73 , m_client(client)
74{
75 dataLogLnIf(WasmStreamingParserInternal::verbose, "starting validation");
76}
77
78auto StreamingParser::parseModuleHeader(Vector<uint8_t>&& data) -> State
79{
80 ASSERT(data.size() == moduleHeaderSize);
81 dataLogLnIf(WasmStreamingParserInternal::verbose, "header validation");
82 WASM_PARSER_FAIL_IF(data[0] != '\0' || data[1] != 'a' || data[2] != 's' || data[3] != 'm', "modules doesn't start with '\\0asm'");
83 uint32_t versionNumber = WTF::unalignedLoad<uint32_t>(data.data() + 4);
84 WASM_PARSER_FAIL_IF(versionNumber != expectedVersionNumber, "unexpected version number ", versionNumber, " expected ", expectedVersionNumber);
85 return State::SectionID;
86}
87
88auto StreamingParser::parseSectionID(Vector<uint8_t>&& data) -> State
89{
90 ASSERT(data.size() == sectionIDSize);
91 size_t offset = 0;
92 auto result = parseUInt7(data.data(), offset, data.size());
93 WASM_PARSER_FAIL_IF(!result, "can't get section byte");
94
95 Section section = Section::Custom;
96 WASM_PARSER_FAIL_IF(!decodeSection(*result, section), "invalid section");
97 ASSERT(section != Section::Begin);
98 WASM_PARSER_FAIL_IF(!validateOrder(m_previousKnownSection, section), "invalid section order, ", m_previousKnownSection, " followed by ", section);
99 m_section = section;
100 if (isKnownSection(section))
101 m_previousKnownSection = section;
102 return State::SectionSize;
103}
104
105auto StreamingParser::parseSectionSize(uint32_t sectionLength) -> State
106{
107 m_sectionLength = sectionLength;
108 if (m_section == Section::Code)
109 return State::CodeSectionSize;
110 return State::SectionPayload;
111}
112
113auto StreamingParser::parseCodeSectionSize(uint32_t functionCount) -> State
114{
115 m_info->codeSectionSize = m_sectionLength;
116 m_functionCount = functionCount;
117 m_functionIndex = 0;
118 m_codeOffset = m_offset;
119
120 WASM_PARSER_FAIL_IF(functionCount == std::numeric_limits<uint32_t>::max(), "Code section's count is too big ", functionCount);
121 WASM_PARSER_FAIL_IF(functionCount != m_info->functions.size(), "Code section count ", functionCount, " exceeds the declared number of functions ", m_info->functions.size());
122
123 if (m_functionIndex == m_functionCount) {
124 WASM_PARSER_FAIL_IF((m_codeOffset + m_sectionLength) != m_nextOffset, "parsing ended before the end of ", m_section, " section");
125 if (!m_client.didReceiveSectionData(m_section))
126 return State::FatalError;
127 return State::SectionID;
128 }
129 return State::FunctionSize;
130}
131
132auto StreamingParser::parseFunctionSize(uint32_t functionSize) -> State
133{
134 m_functionSize = functionSize;
135 WASM_PARSER_FAIL_IF(functionSize > maxFunctionSize, "Code function's size ", functionSize, " is too big");
136 return State::FunctionPayload;
137}
138
139auto StreamingParser::parseFunctionPayload(Vector<uint8_t>&& data) -> State
140{
141 auto& function = m_info->functions[m_functionIndex];
142 function.start = m_offset;
143 function.end = m_offset + m_functionSize;
144 function.data = WTFMove(data);
145 dataLogLnIf(WasmStreamingParserInternal::verbose, "Processing function starting at: ", function.start, " and ending at: ", function.end);
146 if (!m_client.didReceiveFunctionData(m_functionIndex, function))
147 return State::FatalError;
148 ++m_functionIndex;
149
150 if (m_functionIndex == m_functionCount) {
151 WASM_PARSER_FAIL_IF((m_codeOffset + m_sectionLength) != (m_offset + m_functionSize), "parsing ended before the end of ", m_section, " section");
152 if (!m_client.didReceiveSectionData(m_section))
153 return State::FatalError;
154 return State::SectionID;
155 }
156 return State::FunctionSize;
157}
158
159auto StreamingParser::parseSectionPayload(Vector<uint8_t>&& data) -> State
160{
161 SectionParser parser(data.data(), data.size(), m_offset, m_info.get());
162 switch (m_section) {
163#define WASM_SECTION_PARSE(NAME, ID, DESCRIPTION) \
164 case Section::NAME: { \
165 WASM_STREAMING_PARSER_FAIL_IF_HELPER_FAILS(parser.parse ## NAME()); \
166 break; \
167 }
168 FOR_EACH_KNOWN_WASM_SECTION(WASM_SECTION_PARSE)
169#undef WASM_SECTION_PARSE
170
171 case Section::Custom: {
172 WASM_STREAMING_PARSER_FAIL_IF_HELPER_FAILS(parser.parseCustom());
173 break;
174 }
175
176 case Section::Begin: {
177 RELEASE_ASSERT_NOT_REACHED();
178 break;
179 }
180 }
181
182 WASM_PARSER_FAIL_IF(parser.length() != parser.offset(), "parsing ended before the end of ", m_section, " section");
183
184 if (!m_client.didReceiveSectionData(m_section))
185 return State::FatalError;
186 return State::SectionID;
187}
188
189auto StreamingParser::consume(const uint8_t* bytes, size_t bytesSize, size_t& offsetInBytes, size_t requiredSize) -> Optional<Vector<uint8_t>>
190{
191 if (m_remaining.size() == requiredSize) {
192 Vector<uint8_t> result = WTFMove(m_remaining);
193 m_nextOffset += requiredSize;
194 return result;
195 }
196
197 if (m_remaining.size() > requiredSize) {
198 Vector<uint8_t> result(requiredSize);
199 memcpy(result.data(), m_remaining.data(), requiredSize);
200 m_remaining.remove(0, requiredSize);
201 m_nextOffset += requiredSize;
202 return result;
203 }
204
205 ASSERT(m_remaining.size() < requiredSize);
206 size_t bytesRemainingSize = bytesSize - offsetInBytes;
207 size_t totalDataSize = m_remaining.size() + bytesRemainingSize;
208 if (totalDataSize < requiredSize) {
209 m_remaining.append(bytes + offsetInBytes, bytesRemainingSize);
210 offsetInBytes = bytesSize;
211 return WTF::nullopt;
212 }
213
214 size_t usedSize = requiredSize - m_remaining.size();
215 m_remaining.append(bytes + offsetInBytes, usedSize);
216 offsetInBytes += usedSize;
217 Vector<uint8_t> result = WTFMove(m_remaining);
218 m_nextOffset += requiredSize;
219 return result;
220}
221
222auto StreamingParser::consumeVarUInt32(const uint8_t* bytes, size_t bytesSize, size_t& offsetInBytes, IsEndOfStream isEndOfStream) -> Expected<uint32_t, State>
223{
224 constexpr size_t maxSize = WTF::LEBDecoder::maxByteLength<uint32_t>();
225 size_t bytesRemainingSize = bytesSize - offsetInBytes;
226 size_t totalDataSize = m_remaining.size() + bytesRemainingSize;
227 if (m_remaining.size() >= maxSize) {
228 // Do nothing.
229 } else if (totalDataSize >= maxSize) {
230 size_t usedSize = maxSize - m_remaining.size();
231 m_remaining.append(bytes + offsetInBytes, usedSize);
232 offsetInBytes += usedSize;
233 } else {
234 m_remaining.append(bytes + offsetInBytes, bytesRemainingSize);
235 offsetInBytes += bytesRemainingSize;
236 // If the given bytes are the end of the stream, we try to parse VarUInt32
237 // with the current remaining data since VarUInt32 may not require `maxSize`.
238 if (isEndOfStream == IsEndOfStream::No)
239 return makeUnexpected(m_state);
240 }
241
242 size_t offset = 0;
243 uint32_t result = 0;
244 if (!WTF::LEBDecoder::decodeUInt32(m_remaining.data(), m_remaining.size(), offset, result))
245 return makeUnexpected(State::FatalError);
246 size_t consumedSize = offset;
247 m_remaining.remove(0, consumedSize);
248 m_nextOffset += consumedSize;
249 return result;
250}
251
252auto StreamingParser::addBytes(const uint8_t* bytes, size_t bytesSize, IsEndOfStream isEndOfStream) -> State
253{
254 if (m_state == State::FatalError)
255 return m_state;
256
257 m_totalSize += bytesSize;
258 if (UNLIKELY(m_totalSize.hasOverflowed() || m_totalSize.unsafeGet() > maxModuleSize)) {
259 m_state = fail("module size is too large, maximum ", maxModuleSize);
260 return m_state;
261 }
262
263 if (UNLIKELY(Options::useEagerWebAssemblyModuleHashing()))
264 m_hasher.addBytes(bytes, bytesSize);
265
266 size_t offsetInBytes = 0;
267 while (true) {
268 ASSERT(offsetInBytes <= bytesSize);
269 switch (m_state) {
270 case State::ModuleHeader: {
271 auto result = consume(bytes, bytesSize, offsetInBytes, moduleHeaderSize);
272 if (!result)
273 return m_state;
274 m_state = parseModuleHeader(WTFMove(*result));
275 break;
276 }
277
278 case State::SectionID: {
279 auto result = consume(bytes, bytesSize, offsetInBytes, sectionIDSize);
280 if (!result)
281 return m_state;
282 m_state = parseSectionID(WTFMove(*result));
283 break;
284 }
285
286 case State::SectionSize: {
287 auto result = consumeVarUInt32(bytes, bytesSize, offsetInBytes, isEndOfStream);
288 if (!result) {
289 if (result.error() == State::FatalError)
290 m_state = failOnState(m_state);
291 else
292 m_state = result.error();
293 return m_state;
294 }
295 m_state = parseSectionSize(*result);
296 break;
297 }
298
299 case State::SectionPayload: {
300 auto result = consume(bytes, bytesSize, offsetInBytes, m_sectionLength);
301 if (!result)
302 return m_state;
303 m_state = parseSectionPayload(WTFMove(*result));
304 break;
305 }
306
307 case State::CodeSectionSize: {
308 auto result = consumeVarUInt32(bytes, bytesSize, offsetInBytes, isEndOfStream);
309 if (!result) {
310 if (result.error() == State::FatalError)
311 m_state = failOnState(m_state);
312 else
313 m_state = result.error();
314 return m_state;
315 }
316 m_state = parseCodeSectionSize(*result);
317 break;
318 }
319
320 case State::FunctionSize: {
321 auto result = consumeVarUInt32(bytes, bytesSize, offsetInBytes, isEndOfStream);
322 if (!result) {
323 if (result.error() == State::FatalError)
324 m_state = failOnState(m_state);
325 else
326 m_state = result.error();
327 return m_state;
328 }
329 m_state = parseFunctionSize(*result);
330 break;
331 }
332
333 case State::FunctionPayload: {
334 auto result = consume(bytes, bytesSize, offsetInBytes, m_functionSize);
335 if (!result)
336 return m_state;
337 m_state = parseFunctionPayload(WTFMove(*result));
338 break;
339 }
340
341 case State::Finished:
342 case State::FatalError:
343 return m_state;
344 }
345
346 m_offset = m_nextOffset;
347 }
348}
349
350auto StreamingParser::failOnState(State) -> State
351{
352 switch (m_state) {
353 case State::ModuleHeader:
354 return fail("expected a module of at least ", moduleHeaderSize, " bytes");
355 case State::SectionID:
356 return fail("can't get section byte");
357 case State::SectionSize:
358 return fail("can't get ", m_section, " section's length");
359 case State::SectionPayload:
360 return fail(m_section, " section of size ", m_sectionLength, " would overflow Module's size");
361 case State::CodeSectionSize:
362 return fail("can't get Code section's count");
363 case State::FunctionSize:
364 return fail("can't get ", m_functionIndex, "th Code function's size");
365 case State::FunctionPayload:
366 return fail("Code function's size ", m_functionSize, " exceeds the module's remaining size");
367 case State::Finished:
368 case State::FatalError:
369 return m_state;
370 }
371 return m_state;
372}
373
374auto StreamingParser::finalize() -> State
375{
376 addBytes(nullptr, 0, IsEndOfStream::Yes);
377 switch (m_state) {
378 case State::ModuleHeader:
379 case State::SectionSize:
380 case State::SectionPayload:
381 case State::CodeSectionSize:
382 case State::FunctionSize:
383 case State::FunctionPayload:
384 m_state = failOnState(m_state);
385 break;
386
387 case State::Finished:
388 case State::FatalError:
389 break;
390
391 case State::SectionID:
392 if (m_functionIndex != m_info->functions.size()) {
393 m_state = fail("Number of functions parsed (", m_functionCount, ") does not match the number of declared functions (", m_info->functions.size(), ")");
394 break;
395 }
396 if (m_remaining.isEmpty()) {
397 if (UNLIKELY(Options::useEagerWebAssemblyModuleHashing()))
398 m_info->nameSection->setHash(m_hasher.computeHexDigest());
399 m_state = State::Finished;
400 m_client.didFinishParsing();
401 } else
402 m_state = failOnState(State::SectionID);
403 break;
404 }
405 return m_state;
406}
407
408} } // namespace JSC::Wasm
409
410#endif // ENABLE(WEBASSEMBLY)
411