Skip to content

Commit 15e3396

Browse files
Validation: Check stack height at the end of block frames
1 parent 6079c2d commit 15e3396

File tree

5 files changed

+52
-26
lines changed

5 files changed

+52
-26
lines changed

Sources/WasmKit/Translator.swift

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -310,14 +310,17 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
310310
var kind: Kind
311311
var reachable: Bool = true
312312

313-
var copyCount: UInt16 {
313+
var copyTypes: [ValueType] {
314314
switch self.kind {
315315
case .block, .if:
316-
return UInt16(blockType.results.count)
316+
return blockType.results
317317
case .loop:
318-
return UInt16(blockType.parameters.count)
318+
return blockType.parameters
319319
}
320320
}
321+
var copyCount: UInt16 {
322+
return UInt16(copyTypes.count)
323+
}
321324
}
322325

323326
private var frames: [ControlFrame] = []
@@ -893,8 +896,7 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
893896
///
894897
/// - Parameter typeHint: A type expected to be popped. Only used for diagnostic purpose.
895898
/// - Returns: `true` if check succeed. `false` if the pop operation is going to be performed in unreachable code path.
896-
private func checkBeforePop(typeHint: ValueType?) throws -> Bool {
897-
let controlFrame = try controlStack.currentFrame()
899+
private func checkBeforePop(typeHint: ValueType?, controlFrame: ControlStack.ControlFrame) throws -> Bool {
898900
if _slowPath(valueStack.height <= controlFrame.stackHeight) {
899901
if controlFrame.reachable {
900902
let message: String
@@ -910,6 +912,10 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
910912
}
911913
return true
912914
}
915+
private func checkBeforePop(typeHint: ValueType?) throws -> Bool {
916+
let controlFrame = try controlStack.currentFrame()
917+
return try self.checkBeforePop(typeHint: typeHint, controlFrame: controlFrame)
918+
}
913919
private mutating func ensureOnVReg(_ source: ValueSource) -> VReg {
914920
// TODO: Copy to stack if source is on preg
915921
// let copyTo = valueStack.stackRegBase + VReg(valueStack.height)
@@ -959,6 +965,27 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
959965
return try valueStack.pop()
960966
}
961967

968+
@discardableResult
969+
private mutating func popPushValues(_ valueTypes: [ValueType]) throws -> Int {
970+
var values: [ValueSource?] = []
971+
for type in valueTypes.reversed() {
972+
values.append(try popOperand(type))
973+
}
974+
let stackHeight = self.valueStack.height
975+
for (type, value) in zip(valueTypes, values.reversed()) {
976+
switch value {
977+
case .local(let localIndex):
978+
// Re-push local variables to the stack
979+
_ = try valueStack.pushLocal(localIndex, locals: &locals)
980+
case .vreg, nil:
981+
_ = valueStack.push(type)
982+
case .const(let index, let type):
983+
valueStack.pushConst(index, type: type)
984+
}
985+
}
986+
return stackHeight
987+
}
988+
962989
private mutating func visitReturnLike() throws {
963990
preserveOnStack(depth: self.type.results.count)
964991
for (index, resultType) in self.type.results.enumerated().reversed() {
@@ -1050,22 +1077,7 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
10501077
mutating func visitBlock(blockType: WasmParser.BlockType) throws -> Output {
10511078
let blockType = try module.resolveBlockType(blockType)
10521079
let endLabel = iseqBuilder.allocLabel()
1053-
var parameters: [ValueSource?] = []
1054-
for param in blockType.parameters.reversed() {
1055-
parameters.append(try popOperand(param))
1056-
}
1057-
let stackHeight = self.valueStack.height
1058-
for (param, value) in zip(blockType.parameters, parameters.reversed()) {
1059-
switch value {
1060-
case .local(let localIndex):
1061-
// Re-push local variables to the stack
1062-
_ = try valueStack.pushLocal(localIndex, locals: &locals)
1063-
case .vreg, nil:
1064-
_ = valueStack.push(param)
1065-
case .const(let index, let type):
1066-
valueStack.pushConst(index, type: type)
1067-
}
1068-
}
1080+
let stackHeight = try popPushValues(blockType.parameters)
10691081
controlStack.pushFrame(ControlStack.ControlFrame(blockType: blockType, stackHeight: stackHeight, continuation: endLabel, kind: .block))
10701082
}
10711083

@@ -1158,6 +1170,10 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
11581170
if case .block(root: true) = poppedFrame.kind {
11591171
if poppedFrame.reachable {
11601172
try translateReturn()
1173+
// TODO: Merge logic with regular block frame
1174+
guard valueStack.height == poppedFrame.stackHeight else {
1175+
throw ValidationError("values remaining on stack at end of block")
1176+
}
11611177
}
11621178
try iseqBuilder.pinLabelHere(poppedFrame.continuation)
11631179
return
@@ -1173,7 +1189,13 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
11731189
case .if:
11741190
try iseqBuilder.pinLabelHere(poppedFrame.continuation)
11751191
}
1176-
try valueStack.truncate(height: poppedFrame.stackHeight)
1192+
for result in poppedFrame.blockType.results.reversed() {
1193+
guard try checkBeforePop(typeHint: result, controlFrame: poppedFrame) else { continue }
1194+
_ = try valueStack.pop(result)
1195+
}
1196+
guard valueStack.height == poppedFrame.stackHeight else {
1197+
throw ValidationError("values remaining on stack at end of block")
1198+
}
11771199
for result in poppedFrame.blockType.results {
11781200
_ = valueStack.push(result)
11791201
}
@@ -1231,6 +1253,9 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
12311253
try emitBranch(Instruction.br, relativeDepth: relativeDepth) { offset, copyCount, popCount in
12321254
return offset
12331255
}
1256+
for type in frame.copyTypes.reversed() {
1257+
_ = try popOperand(type)
1258+
}
12341259
try markUnreachable()
12351260
}
12361261

@@ -1279,12 +1304,12 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
12791304
try emitBranch(Instruction.br, relativeDepth: relativeDepth) { offset, copyCount, popCount in
12801305
return offset
12811306
}
1307+
try popPushValues(frame.copyTypes)
12821308
try iseqBuilder.pinLabelHere(onBranchNotTaken)
12831309
}
12841310

12851311
mutating func visitBrTable(targets: WasmParser.BrTable) throws -> Output {
12861312
guard let index = try popVRegOperand(.i32) else { return }
1287-
guard try controlStack.currentFrame().reachable else { return }
12881313

12891314
let defaultFrame = try controlStack.branchTarget(relativeDepth: targets.defaultIndex)
12901315

Tests/WasmKitTests/ExtraSuite/const_slot.wast

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
;; emit its own instruction.
1818
(i32.const 0)
1919
(local.set 0)
20+
(drop) ;; drop i32.add
2021
(local.get 0)
2122
)
2223
)

Tests/WasmKitTests/ExtraSuite/local_cow.wast

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
;; emit its own instruction.
4444
(local.get 1)
4545
(local.set 0)
46+
(drop) ;; drop i32.add
4647
(local.get 0)
4748
)
4849
)

Tests/WasmKitTests/Spectest/TestCase.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ extension TestCase {
143143
if let result = try context.run(directive: directive) {
144144
handler(self, location, result)
145145
}
146-
} catch let error as SpectestError {
147-
handler(self, location, .failed(error.description))
146+
} catch let error {
147+
handler(self, location, .failed("\(error)"))
148148
}
149149
}
150150
} catch let parseError as WatParserError {

Tests/WasmKitTests/SpectestTests.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ final class SpectestTests: XCTestCase {
2222
path: Self.testPaths,
2323
include: [],
2424
exclude: [
25-
"block.wast",
2625
"br.wast",
2726
"br_if.wast",
2827
"call.wast",

0 commit comments

Comments
 (0)