WebAssembly: implement data section
[WebKit-https.git] / Source / JavaScriptCore / wasm / WasmModuleParser.cpp
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
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 "WasmModuleParser.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "IdentifierInlines.h"
32 #include "WasmFormat.h"
33 #include "WasmMemoryInformation.h"
34 #include "WasmOps.h"
35 #include "WasmSections.h"
36
37 #include <sys/mman.h>
38
39 namespace JSC { namespace Wasm {
40
41 static const bool verbose = false;
42
43 bool ModuleParser::parse()
44 {
45     m_module = std::make_unique<ModuleInformation>();
46
47     const size_t minSize = 8;
48     if (length() < minSize) {
49         m_errorMessage = "Module is " + String::number(length()) + " bytes, expected at least " + String::number(minSize) + " bytes";
50         return false;
51     }
52     if (!consumeCharacter(0) || !consumeString("asm")) {
53         m_errorMessage = "Modules doesn't start with '\\0asm'";
54         return false;
55     }
56
57     uint32_t versionNumber;
58     if (!parseUInt32(versionNumber)) {
59         // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
60         m_errorMessage = "couldn't parse version number";
61         return false;
62     }
63
64     if (versionNumber != expectedVersionNumber) {
65         // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
66         m_errorMessage = "unexpected version number";
67         return false;
68     }
69
70
71     if (verbose)
72         dataLogLn("Passed processing header.");
73
74     Sections::Section previousSection = Sections::Unknown;
75     while (m_offset < length()) {
76         if (verbose)
77             dataLogLn("Starting to parse next section at offset: ", m_offset);
78
79         uint8_t sectionByte;
80         if (!parseUInt7(sectionByte)) {
81             // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
82             m_errorMessage = "couldn't get section byte";
83             return false;
84         }
85
86         if (verbose)
87             dataLogLn("Section byte: ", sectionByte);
88
89         Sections::Section section = Sections::Unknown;
90         if (sectionByte) {
91             if (sectionByte < Sections::Unknown)
92                 section = static_cast<Sections::Section>(sectionByte);
93         }
94
95         if (!Sections::validateOrder(previousSection, section)) {
96             // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
97             m_errorMessage = "invalid section order";
98             return false;
99         }
100
101         uint32_t sectionLength;
102         if (!parseVarUInt32(sectionLength)) {
103             // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
104             m_errorMessage = "couldn't get section length";
105             return false;
106         }
107
108         if (sectionLength > length() - m_offset) {
109             // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
110             m_errorMessage = "section content would overflow Module's size";
111             return false;
112         }
113
114         auto end = m_offset + sectionLength;
115
116         switch (section) {
117         // FIXME improve error message in macro below https://bugs.webkit.org/show_bug.cgi?id=163919
118 #define WASM_SECTION_PARSE(NAME, ID, DESCRIPTION) \
119         case Sections::NAME: { \
120             if (verbose) \
121                 dataLogLn("Parsing " DESCRIPTION); \
122             if (!parse ## NAME()) { \
123                 m_errorMessage = "couldn't parse section " #NAME ": " DESCRIPTION; \
124                 return false; \
125             } \
126             break; \
127         }
128         FOR_EACH_WASM_SECTION(WASM_SECTION_PARSE)
129 #undef WASM_SECTION_PARSE
130
131         case Sections::Unknown: {
132             if (verbose)
133                 dataLogLn("Unknown section, skipping.");
134             // Ignore section's name LEB and bytes: they're already included in sectionLength.
135             m_offset += sectionLength;
136             break;
137         }
138         }
139
140         if (verbose)
141             dataLogLn("Finished parsing section.");
142
143         if (end != m_offset) {
144             // FIXME improve error message https://bugs.webkit.org/show_bug.cgi?id=163919
145             m_errorMessage = "parsing ended before the end of the section";
146             return false;
147         }
148
149         previousSection = section;
150     }
151
152     // TODO
153     m_failed = false;
154     return true;
155 }
156
157 bool ModuleParser::parseType()
158 {
159     uint32_t count;
160     if (!parseVarUInt32(count)
161         || count == std::numeric_limits<uint32_t>::max()
162         || !m_module->signatures.tryReserveCapacity(count))
163         return false;
164     if (verbose)
165         dataLogLn("  count: ", count);
166
167     for (uint32_t i = 0; i < count; ++i) {
168         int8_t type;
169         if (!parseInt7(type))
170             return false;
171         if (type != Func)
172             return false;
173
174         if (verbose)
175             dataLogLn("Got function type.");
176
177         uint32_t argumentCount;
178         Vector<Type> argumentTypes;
179         if (!parseVarUInt32(argumentCount)
180             || argumentCount == std::numeric_limits<uint32_t>::max()
181             || !argumentTypes.tryReserveCapacity(argumentCount))
182             return false;
183         if (verbose)
184             dataLogLn("  argument count: ", argumentCount);
185
186         for (unsigned i = 0; i < argumentCount; ++i) {
187             Type argumentType;
188             if (!parseResultType(argumentType))
189                 return false;
190             argumentTypes.uncheckedAppend(argumentType);
191         }
192
193         uint8_t returnCount;
194         if (!parseVarUInt1(returnCount))
195             return false;
196         Type returnType;
197
198         if (verbose)
199             dataLogLn(returnCount);
200
201         if (returnCount) {
202             Type value;
203             if (!parseValueType(value))
204                 return false;
205             returnType = static_cast<Type>(value);
206         } else
207             returnType = Type::Void;
208
209         m_module->signatures.uncheckedAppend({ returnType, WTFMove(argumentTypes) });
210     }
211     return true;
212 }
213
214 bool ModuleParser::parseImport()
215 {
216     uint32_t importCount;
217     if (!parseVarUInt32(importCount)
218         || importCount == std::numeric_limits<uint32_t>::max()
219         || !m_module->imports.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.
220         || !m_module->importFunctions.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.
221         || !m_functionIndexSpace.tryReserveCapacity(importCount)) // FIXME this over-allocates when we fix the FIXMEs below. We'll allocate some more here when we know how many functions to expect.
222         return false;
223
224     for (uint32_t importNumber = 0; importNumber < importCount; ++importNumber) {
225         Import imp;
226         uint32_t moduleLen;
227         uint32_t fieldLen;
228         String moduleString;
229         String fieldString;
230         if (!parseVarUInt32(moduleLen)
231             || !consumeUTF8String(moduleString, moduleLen))
232             return false;
233         imp.module = Identifier::fromString(m_vm, moduleString);
234         if (!parseVarUInt32(fieldLen)
235             || !consumeUTF8String(fieldString, fieldLen))
236             return false;
237         imp.field = Identifier::fromString(m_vm, fieldString);
238         if (!parseExternalKind(imp.kind))
239             return false;
240         switch (imp.kind) {
241         case External::Function: {
242             uint32_t functionSignatureIndex;
243             if (!parseVarUInt32(functionSignatureIndex)
244                 || functionSignatureIndex >= m_module->signatures.size())
245                 return false;
246             imp.kindIndex = m_module->importFunctions.size();
247             Signature* signature = &m_module->signatures[functionSignatureIndex];
248             m_module->importFunctions.uncheckedAppend(signature);
249             m_functionIndexSpace.uncheckedAppend(signature);
250             break;
251         }
252         case External::Table: {
253             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164135
254             break;
255         }
256         case External::Memory: {
257             bool isImport = true;
258             if (!parseMemoryHelper(isImport))
259                 return false;
260             break;
261         }
262         case External::Global: {
263             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
264             // In the MVP, only immutable global variables can be imported.
265             break;
266         }
267         }
268
269         m_module->imports.uncheckedAppend(imp);
270     }
271
272     return true;
273 }
274
275 bool ModuleParser::parseFunction()
276 {
277     uint32_t count;
278     if (!parseVarUInt32(count)
279         || count == std::numeric_limits<uint32_t>::max()
280         || !m_module->internalFunctionSignatures.tryReserveCapacity(count)
281         || !m_functionLocationInBinary.tryReserveCapacity(count)
282         || !m_functionIndexSpace.tryReserveCapacity(m_functionIndexSpace.size() + count))
283         return false;
284
285     for (uint32_t i = 0; i < count; ++i) {
286         uint32_t typeNumber;
287         if (!parseVarUInt32(typeNumber)
288             || typeNumber >= m_module->signatures.size())
289             return false;
290
291         Signature* signature = &m_module->signatures[typeNumber];
292         // The Code section fixes up start and end.
293         size_t start = 0;
294         size_t end = 0;
295         m_module->internalFunctionSignatures.uncheckedAppend(signature);
296         m_functionLocationInBinary.uncheckedAppend({ start, end });
297         m_functionIndexSpace.uncheckedAppend(signature);
298     }
299
300     return true;
301 }
302
303 bool ModuleParser::parseTable()
304 {
305     // FIXME implement table https://bugs.webkit.org/show_bug.cgi?id=164135
306     RELEASE_ASSERT_NOT_REACHED();
307     return true;
308 }
309
310 bool ModuleParser::parseMemoryHelper(bool isImport)
311 {
312     // We don't allow redeclaring memory. Either via import or definition.
313     if (m_module->memory)
314         return false;
315
316     uint8_t flags;
317     if (!parseVarUInt1(flags))
318         return false;
319
320     uint32_t initial;
321     if (!parseVarUInt32(initial))
322         return false;
323
324     if (!PageCount::isValid(initial))
325         return false;
326
327     PageCount initialPageCount(initial);
328
329     PageCount maximumPageCount;
330     if (flags) {
331         uint32_t maximum;
332         if (!parseVarUInt32(maximum))
333             return false;
334
335         if (!PageCount::isValid(maximum))
336             return false;
337
338         maximumPageCount = PageCount(maximum);
339         if (initialPageCount > maximumPageCount)
340             return false;
341     }
342
343     Vector<unsigned> pinnedSizes = { 0 };
344     m_module->memory = MemoryInformation(initialPageCount, maximumPageCount, pinnedSizes, isImport);
345     return true;
346 }
347
348 bool ModuleParser::parseMemory()
349 {
350     uint8_t count;
351     if (!parseVarUInt1(count))
352         return false;
353
354     if (!count)
355         return true;
356
357     // We only allow one memory for now.
358     if (count != 1)
359         return false;
360
361     bool isImport = false;
362     return parseMemoryHelper(isImport);
363 }
364
365 bool ModuleParser::parseGlobal()
366 {
367     // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
368     RELEASE_ASSERT_NOT_REACHED();
369     return true;
370 }
371
372 bool ModuleParser::parseExport()
373 {
374     uint32_t exportCount;
375     if (!parseVarUInt32(exportCount)
376         || exportCount == std::numeric_limits<uint32_t>::max()
377         || !m_module->exports.tryReserveCapacity(exportCount))
378         return false;
379
380     for (uint32_t exportNumber = 0; exportNumber < exportCount; ++exportNumber) {
381         Export exp;
382         uint32_t fieldLen;
383         String fieldString;
384         if (!parseVarUInt32(fieldLen)
385             || !consumeUTF8String(fieldString, fieldLen))
386             return false;
387         exp.field = Identifier::fromString(m_vm, fieldString);
388
389         if (!parseExternalKind(exp.kind))
390             return false;
391
392         switch (exp.kind) {
393         case External::Function: {
394             if (!parseVarUInt32(exp.functionIndex)
395                 || exp.functionIndex >= m_functionIndexSpace.size())
396                 return false;
397             break;
398         }
399         case External::Table: {
400             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164135
401             break;
402         }
403         case External::Memory: {
404             // FIXME: https://bugs.webkit.org/show_bug.cgi?id=165671
405             break;
406         }
407         case External::Global: {
408             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
409             // In the MVP, only immutable global variables can be exported.
410             break;
411         }
412         }
413
414         m_module->exports.uncheckedAppend(exp);
415     }
416
417     return true;
418 }
419
420 bool ModuleParser::parseStart()
421 {
422     uint32_t startFunctionIndex;
423     if (!parseVarUInt32(startFunctionIndex)
424         || startFunctionIndex >= m_functionIndexSpace.size())
425         return false;
426     Signature* signature = m_functionIndexSpace[startFunctionIndex].signature;
427     if (signature->arguments.size() != 0
428         || signature->returnType != Void)
429         return false;
430     m_module->startFunctionIndexSpace = startFunctionIndex;
431     return true;
432 }
433
434 bool ModuleParser::parseElement()
435 {
436     // FIXME https://bugs.webkit.org/show_bug.cgi?id=161709
437     RELEASE_ASSERT_NOT_REACHED();
438     return true;
439 }
440
441 bool ModuleParser::parseCode()
442 {
443     uint32_t count;
444     if (!parseVarUInt32(count)
445         || count == std::numeric_limits<uint32_t>::max()
446         || count != m_functionLocationInBinary.size())
447         return false;
448
449     for (uint32_t i = 0; i < count; ++i) {
450         uint32_t functionSize;
451         if (!parseVarUInt32(functionSize)
452             || functionSize > length()
453             || functionSize > length() - m_offset)
454             return false;
455
456         m_functionLocationInBinary[i].start = m_offset;
457         m_functionLocationInBinary[i].end = m_offset + functionSize;
458         m_offset = m_functionLocationInBinary[i].end;
459     }
460
461     return true;
462 }
463
464 bool ModuleParser::parseData()
465 {
466     uint32_t segmentCount;
467     if (!parseVarUInt32(segmentCount)
468         || segmentCount == std::numeric_limits<uint32_t>::max()
469         || !m_module->data.tryReserveCapacity(segmentCount))
470         return false;
471     if (verbose)
472         dataLogLn("  segments: ", segmentCount);
473
474     for (uint32_t segmentNumber = 0; segmentNumber < segmentCount; ++segmentNumber) {
475         if (verbose)
476             dataLogLn("  segment #", segmentNumber);
477         uint32_t index;
478         uint8_t opcode;
479         uint32_t offset;
480         uint8_t endOpcode;
481         uint32_t dataByteLength;
482         if (!parseVarUInt32(index)
483             || index)
484             return false;
485
486         // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700
487         // For now we only handle i32.const as offset.
488         if (!parseUInt8(opcode)
489             || opcode != Wasm::I32Const
490             || !parseVarUInt32(offset)
491             || !parseUInt8(endOpcode)
492             || endOpcode != Wasm::End)
493             return false;
494         if (verbose)
495             dataLogLn("    offset: ", offset);
496
497         if (!parseVarUInt32(dataByteLength)
498             || dataByteLength == std::numeric_limits<uint32_t>::max())
499             return false;
500         if (verbose)
501             dataLogLn("    data bytes: ", dataByteLength);
502
503         Segment* segment = Segment::make(offset, dataByteLength);
504         if (!segment)
505             return false;
506         m_module->data.uncheckedAppend(Segment::makePtr(segment));
507         for (uint32_t dataByte = 0; dataByte < dataByteLength; ++dataByte) {
508             uint8_t byte;
509             if (!parseUInt8(byte))
510                 return false;
511             segment->byte(dataByte) = byte;
512             if (verbose)
513                 dataLogLn("    [", dataByte, "] = ", segment->byte(dataByte));
514         }
515     }
516     return true;
517 }
518
519 } } // namespace JSC::Wasm
520
521 #endif // ENABLE(WEBASSEMBLY)