Skip to content

Commit 7bcf810

Browse files
WAT: Ban too large offset field in memarg position
1 parent ce85218 commit 7bcf810

File tree

8 files changed

+55
-33
lines changed

8 files changed

+55
-33
lines changed

Sources/WAT/Encoder.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ struct Encoder {
8888
}
8989

9090
mutating func writeExpression(lexer: inout Lexer, wat: inout Wat) throws {
91-
var parser = ExpressionParser<ExpressionEncoder>(lexer: lexer)
91+
var parser = ExpressionParser<ExpressionEncoder>(lexer: lexer, features: wat.features)
9292
var exprEncoder = ExpressionEncoder()
9393
try parser.parse(visitor: &exprEncoder, wat: &wat)
9494
try exprEncoder.visitEnd()
@@ -156,7 +156,7 @@ struct ElementExprCollector: AnyInstructionVisitor {
156156
mutating func parse(indices: WatParser.ElementDecl.Indices, wat: inout Wat) throws {
157157
switch indices {
158158
case .elementExprList(let lexer):
159-
var parser = ExpressionParser<ElementExprCollector>(lexer: lexer)
159+
var parser = ExpressionParser<ElementExprCollector>(lexer: lexer, features: wat.features)
160160
try parser.parseElemExprList(visitor: &self, wat: &wat)
161161
case .functionList(let lexer):
162162
try self.parseFunctionList(lexer: lexer, wat: wat)
@@ -557,7 +557,7 @@ func encode(module: inout Wat) throws -> [UInt8] {
557557
encoder.writeUnsignedLEB128(local.count)
558558
local.type.encode(to: &encoder)
559559
}
560-
let funcTypeIndex = try function.parse(visitor: &exprEncoder, wat: &module)
560+
let funcTypeIndex = try function.parse(visitor: &exprEncoder, wat: &module, features: module.features)
561561
functionSection.append(UInt32(funcTypeIndex))
562562
// TODO?
563563
try exprEncoder.visitEnd()

Sources/WAT/Parser/ExpressionParser.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,24 @@ struct ExpressionParser<Visitor: InstructionVisitor> {
3636
}
3737
var parser: Parser
3838
let locals: LocalsMap
39+
let features: WasmFeatureSet
3940
private var labelStack = LabelStack()
4041

4142
init(
4243
type: WatParser.FunctionType,
4344
locals: [WatParser.LocalDecl],
44-
lexer: Lexer
45+
lexer: Lexer,
46+
features: WasmFeatureSet
4547
) throws {
4648
self.parser = Parser(lexer)
4749
self.locals = try Self.computeLocals(type: type, locals: locals)
50+
self.features = features
4851
}
4952

50-
init(lexer: Lexer) {
53+
init(lexer: Lexer, features: WasmFeatureSet) {
5154
self.parser = Parser(lexer)
5255
self.locals = LocalsMap()
56+
self.features = features
5357
}
5458

5559
static func computeLocals(type: WatParser.FunctionType, locals: [WatParser.LocalDecl]) throws -> LocalsMap {
@@ -115,7 +119,7 @@ struct ExpressionParser<Visitor: InstructionVisitor> {
115119
mutating func parseWastConstInstruction(
116120
visitor: inout Visitor
117121
) throws -> Bool where Visitor: WastConstInstructionVisitor {
118-
var wat = Wat.empty()
122+
var wat = Wat.empty(features: features)
119123
// WAST allows extra const value instruction
120124
if try parser.takeParenBlockStart("ref.extern") {
121125
_ = try visitor.visitRefExtern(value: parser.expectUnsignedInt())
@@ -130,7 +134,7 @@ struct ExpressionParser<Visitor: InstructionVisitor> {
130134
}
131135

132136
mutating func parseConstInstruction(visitor: inout Visitor) throws -> Bool {
133-
var wat = Wat.empty()
137+
var wat = Wat.empty(features: features)
134138
if try foldedInstruction(visitor: &visitor, wat: &wat) {
135139
return true
136140
}
@@ -383,6 +387,10 @@ struct ExpressionParser<Visitor: InstructionVisitor> {
383387
try parser.consume()
384388
var subParser = Parser(String(maybeOffset.dropFirst(offsetPrefix.count)))
385389
offset = try subParser.expectUnsignedInt(UInt64.self)
390+
391+
if !features.contains(.memory64), offset > UInt32.max {
392+
throw WatParserError("memory offset must be less than or equal to \(UInt32.max)", location: subParser.lexer.location())
393+
}
386394
}
387395
var align: UInt32 = defaultAlign
388396
let alignPrefix = "align="

Sources/WAT/Parser/WastParser.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ protocol WastConstInstructionVisitor: InstructionVisitor {
99
/// You can find its grammar definition in the [WebAssembly spec repository](https://github.com/WebAssembly/spec/blob/wg-1.0/interpreter/README.md#scripts)
1010
struct WastParser {
1111
var parser: Parser
12+
let features: WasmFeatureSet
1213

13-
init(_ input: String) {
14+
init(_ input: String, features: WasmFeatureSet) {
1415
self.parser = Parser(input)
16+
self.features = features
1517
}
1618

1719
mutating func nextDirective() throws -> WastDirective? {
@@ -24,7 +26,7 @@ struct WastParser {
2426
let location = originalParser.lexer.location()
2527
return .module(
2628
ModuleDirective(
27-
source: .text(try parseWAT(&originalParser)), id: nil, location: location
29+
source: .text(try parseWAT(&originalParser, features: features)), id: nil, location: location
2830
))
2931
}
3032
throw WatParserError("unexpected wast directive token", location: parser.lexer.location())
@@ -78,7 +80,7 @@ struct WastParser {
7880
mutating func constExpression() throws -> [Value] {
7981
var values: [Value] = []
8082
var collector = ConstExpressionCollector(addValue: { values.append($0) })
81-
var exprParser = ExpressionParser<ConstExpressionCollector>(lexer: parser.lexer)
83+
var exprParser = ExpressionParser<ConstExpressionCollector>(lexer: parser.lexer, features: features)
8284
while try exprParser.parseWastConstInstruction(visitor: &collector) {}
8385
parser = exprParser.parser
8486
return values
@@ -87,7 +89,7 @@ struct WastParser {
8789
mutating func expectationValues() throws -> [WastExpectValue] {
8890
var values: [WastExpectValue] = []
8991
var collector = ConstExpressionCollector(addValue: { values.append(.value($0)) })
90-
var exprParser = ExpressionParser<ConstExpressionCollector>(lexer: parser.lexer)
92+
var exprParser = ExpressionParser<ConstExpressionCollector>(lexer: parser.lexer, features: features)
9193
while true {
9294
if let expectValue = try exprParser.parseWastExpectValue() {
9395
values.append(expectValue)
@@ -115,7 +117,7 @@ public enum WastExecute {
115117
execute = .invoke(try WastInvoke.parse(wastParser: &wastParser))
116118
case "module":
117119
try wastParser.parser.consume()
118-
execute = .wat(try parseWAT(&wastParser.parser))
120+
execute = .wat(try parseWAT(&wastParser.parser, features: wastParser.features))
119121
try wastParser.parser.skipParenBlock()
120122
case "get":
121123
try wastParser.parser.consume()
@@ -224,9 +226,10 @@ public enum WastDirective {
224226
return .assertExhaustion(call: call, message: message)
225227
case "assert_unlinkable":
226228
try wastParser.parser.consume()
229+
let features = wastParser.features
227230
let module = try wastParser.parens {
228231
try $0.parser.expectKeyword("module")
229-
let wat = try parseWAT(&$0.parser)
232+
let wat = try parseWAT(&$0.parser, features: features)
230233
try $0.parser.skipParenBlock()
231234
return wat
232235
}
@@ -293,7 +296,7 @@ public enum ModuleSource {
293296
}
294297
}
295298

296-
let watModule = try parseWAT(&wastParser.parser)
299+
let watModule = try parseWAT(&wastParser.parser, features: wastParser.features)
297300
try wastParser.parser.skipParenBlock()
298301
return .text(watModule)
299302
}

Sources/WAT/Parser/WatParser.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ struct WatParser {
8080
/// This method may modify TypesMap of the given WATModule
8181
///
8282
/// - Returns: Type index of this function
83-
func parse<V: InstructionVisitor>(visitor: inout V, wat: inout Wat) throws -> Int {
83+
func parse<V: InstructionVisitor>(visitor: inout V, wat: inout Wat, features: WasmFeatureSet) throws -> Int {
8484
guard case let .definition(locals, body) = kind else {
8585
fatalError("Imported functions cannot be parsed")
8686
}
8787
let (type, typeIndex) = try wat.types.resolve(use: typeUse)
88-
var parser = try ExpressionParser<V>(type: type, locals: locals, lexer: body)
88+
var parser = try ExpressionParser<V>(type: type, locals: locals, lexer: body, features: features)
8989
try parser.parse(visitor: &visitor, wat: &wat)
9090
return typeIndex
9191
}

Sources/WAT/WAT.swift

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import WasmParser
1717
/// )
1818
/// """)
1919
/// ```
20-
public func wat2wasm(_ input: String) throws -> [UInt8] {
21-
var wat = try parseWAT(input)
20+
public func wat2wasm(_ input: String, features: WasmFeatureSet = .default) throws -> [UInt8] {
21+
var wat = try parseWAT(input, features: features)
2222
return try encode(module: &wat)
2323
}
2424

@@ -36,10 +36,11 @@ public struct Wat {
3636
let imports: [Import]
3737
let exports: [Export]
3838
let customSections = [CustomSection]()
39+
let features: WasmFeatureSet
3940

4041
let parser: Parser
4142

42-
static func empty() -> Wat {
43+
static func empty(features: WasmFeatureSet) -> Wat {
4344
Wat(
4445
types: TypesMap(),
4546
functionsMap: NameMapping<WatParser.FunctionDecl>(),
@@ -52,6 +53,7 @@ public struct Wat {
5253
start: nil,
5354
imports: [],
5455
exports: [],
56+
features: features,
5557
parser: Parser("")
5658
)
5759
}
@@ -89,15 +91,15 @@ public struct Wat {
8991
///
9092
/// let wasm = try wat.encode()
9193
/// ```
92-
public func parseWAT(_ input: String) throws -> Wat {
94+
public func parseWAT(_ input: String, features: WasmFeatureSet = .default) throws -> Wat {
9395
var parser = Parser(input)
9496
let wat: Wat
9597
if try parser.takeParenBlockStart("module") {
96-
wat = try parseWAT(&parser)
98+
wat = try parseWAT(&parser, features: features)
9799
try parser.skipParenBlock()
98100
} else {
99101
// The root (module) may be omitted
100-
wat = try parseWAT(&parser)
102+
wat = try parseWAT(&parser, features: features)
101103
}
102104
return wat
103105
}
@@ -106,8 +108,8 @@ public func parseWAT(_ input: String) throws -> Wat {
106108
public struct Wast {
107109
var parser: WastParser
108110

109-
init(_ input: String) {
110-
self.parser = WastParser(input)
111+
init(_ input: String, features: WasmFeatureSet) {
112+
self.parser = WastParser(input, features: features)
111113
}
112114

113115
/// Parses the next directive in the WAST script.
@@ -147,11 +149,11 @@ public struct Wast {
147149
/// print("\(location): \(directive)")
148150
/// }
149151
/// ```
150-
public func parseWAST(_ input: String) throws -> Wast {
151-
return Wast(input)
152+
public func parseWAST(_ input: String, features: WasmFeatureSet = .default) throws -> Wast {
153+
return Wast(input, features: features)
152154
}
153155

154-
func parseWAT(_ parser: inout Parser) throws -> Wat {
156+
func parseWAT(_ parser: inout Parser, features: WasmFeatureSet) throws -> Wat {
155157
// This parser is 2-pass: first it collects all module items and creates a mapping of names to indices.
156158

157159
let initialParser = parser
@@ -285,6 +287,7 @@ func parseWAT(_ parser: inout Parser) throws -> Wat {
285287
start: startIndex,
286288
imports: imports,
287289
exports: exports,
290+
features: features,
288291
parser: parser
289292
)
290293
}

Tests/WATTests/EncoderTests.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class EncoderTests: XCTestCase {
2727
}
2828

2929
print("Checking\n wast: \(wast.path)\n json: \(json.path)")
30-
var parser = WastParser(try String(contentsOf: wast))
30+
var parser = WastParser(try String(contentsOf: wast), features: Spectest.deriveFeatureSet(wast: wast))
3131
var watModules: [ModuleDirective] = []
3232

3333
while let directive = try parser.nextDirective() {
@@ -107,7 +107,6 @@ class EncoderTests: XCTestCase {
107107

108108
var stats = CompatibilityTestStats()
109109
let excluded: [String] = [
110-
"address.wast",
111110
"align.wast",
112111
"align64.wast",
113112
"block.wast",

Tests/WATTests/ParserTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import XCTest
55
@testable import WAT
66

77
class ParserTests: XCTestCase {
8-
func parseWast(_ source: String) throws -> [WastDirective] {
9-
var parser = WastParser(source)
8+
func parseWast(_ source: String, features: WasmFeatureSet = .default) throws -> [WastDirective] {
9+
var parser = WastParser(source, features: features)
1010
var directives: [WastDirective] = []
1111
while let directive = try parser.nextDirective() {
1212
directives.append(directive)
@@ -66,7 +66,7 @@ class ParserTests: XCTestCase {
6666
(unknown expr)
6767
)
6868
)
69-
"""#)
69+
"""#, features: .default)
7070

7171
while let directive = try parser.nextDirective() {
7272
switch directive {
@@ -185,7 +185,7 @@ class ParserTests: XCTestCase {
185185
totalCount += 1
186186
let source = try String(contentsOf: filePath)
187187
do {
188-
_ = try parseWast(source)
188+
_ = try parseWast(source, features: Spectest.deriveFeatureSet(wast: filePath))
189189
} catch {
190190
failureCount += 1
191191
XCTFail("Failed to parse \(filePath.path):\(error)")

Tests/WATTests/Spectest.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Foundation
2+
import WasmParser
23

34
enum Spectest {
45
static let rootDirectory = URL(fileURLWithPath: #filePath)
@@ -37,6 +38,14 @@ enum Spectest {
3738
}
3839
}
3940

41+
static func deriveFeatureSet(wast: URL) -> WasmFeatureSet {
42+
var features = WasmFeatureSet.default
43+
if wast.deletingLastPathComponent().path.hasSuffix("proposals/memory64") {
44+
features.insert(.memory64)
45+
}
46+
return features
47+
}
48+
4049
struct Command: Decodable {
4150
enum CommandType: String, Decodable {
4251
case module

0 commit comments

Comments
 (0)