Skip to content

Commit 967e7e7

Browse files
Check value stack height at the entry of function
And also fix the leak of the stack and frame buffers.
1 parent 4b70f1b commit 967e7e7

File tree

6 files changed

+35
-18
lines changed

6 files changed

+35
-18
lines changed

Sources/WasmKit/Execution/Instructions/Expression.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@ import WasmParser
22

33
struct InstructionSequence: Equatable {
44
let instructions: UnsafeBufferPointer<Instruction>
5+
/// The maximum height of the value stack during execution of this function.
6+
/// This height does not count the locals.
7+
let maxStackHeight: Int
58

6-
init(instructions: [Instruction]) {
9+
init(instructions: [Instruction], maxStackHeight: Int) {
710
assert(_isPOD(Instruction.self))
811
let buffer = UnsafeMutableBufferPointer<Instruction>.allocate(capacity: instructions.count + 1)
912
for (idx, instruction) in instructions.enumerated() {
1013
buffer[idx] = instruction
1114
}
1215
buffer[instructions.count] = .endOfFunction
1316
self.instructions = UnsafeBufferPointer(buffer)
17+
self.maxStackHeight = maxStackHeight
1418
}
1519

1620
func deallocate() {
@@ -26,12 +30,6 @@ struct InstructionSequence: Equatable {
2630
}
2731
}
2832

29-
extension InstructionSequence: ExpressibleByArrayLiteral {
30-
init(arrayLiteral elements: Instruction...) {
31-
self.init(instructions: elements)
32-
}
33-
}
34-
3533
struct ExpressionRef: Equatable {
3634
let _relativeOffset: UInt32
3735
var relativeOffset: Int {

Sources/WasmKit/Execution/Runtime/Function.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public struct Function: Equatable {
88
public func invoke(_ arguments: [Value] = [], runtime: Runtime) throws -> [Value] {
99
try withExecution { execution in
1010
var stack = Stack()
11+
defer { stack.deallocate() }
1112
let numberOfResults = try invoke(execution: &execution, stack: &stack, with: arguments, runtime: runtime)
1213
try execution.run(runtime: runtime, stack: &stack)
1314
return Array(stack.popValues(count: numberOfResults))

Sources/WasmKit/Execution/Runtime/Runtime.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,15 @@ extension Runtime {
8888
.tableInit(tableIndex, elementIndex),
8989
.tableElementDrop(elementIndex),
9090
])
91-
let initIseq = InstructionSequence(instructions: instructions)
91+
let initIseq = InstructionSequence(instructions: instructions, maxStackHeight: 2)
9292
defer { initIseq.deallocate() }
9393
try evaluateConstExpr(initIseq, instance: instance)
9494

9595
case .declarative:
96-
let initIseq: InstructionSequence = [.tableElementDrop(elementIndex)]
96+
let initIseq = InstructionSequence(
97+
instructions: [.tableElementDrop(elementIndex)],
98+
maxStackHeight: 0
99+
)
97100
defer { initIseq.deallocate() }
98101
try evaluateConstExpr(initIseq, instance: instance)
99102

@@ -127,7 +130,7 @@ extension Runtime {
127130
.numericConst(.i32(UInt32(data.initializer.count))),
128131
.memoryInit(UInt32(dataIndex)),
129132
.memoryDataDrop(UInt32(dataIndex)),
130-
])
133+
], maxStackHeight: 2)
131134
defer { iseq.deallocate() }
132135
try evaluateConstExpr(iseq, instance: instance)
133136
}
@@ -141,6 +144,7 @@ extension Runtime {
141144
if let startIndex = module.start {
142145
try withExecution { initExecution in
143146
var stack = Stack()
147+
defer { stack.deallocate() }
144148
try initExecution.invoke(functionAddress: instance.functionAddresses[Int(startIndex)], runtime: self, stack: &stack)
145149
try initExecution.run(runtime: self, stack: &stack)
146150
}
@@ -189,7 +193,7 @@ extension Runtime {
189193
default:
190194
throw InstantiationError.unsupported("init expr in global section \(global.initializer)")
191195
}
192-
let iseq = InstructionSequence(instructions: instructions)
196+
let iseq = InstructionSequence(instructions: instructions, maxStackHeight: 1)
193197
defer { iseq.deallocate() }
194198
return try evaluateConstExpr(iseq, instance: globalModuleInstance, arity: 1) { _, stack in
195199
return stack.popValue()
@@ -212,6 +216,7 @@ extension Runtime {
212216
) throws -> T {
213217
try withExecution { initExecution in
214218
var stack = Stack()
219+
defer { stack.deallocate() }
215220
try stack.pushFrame(
216221
iseq: iseq,
217222
arity: arity,

Sources/WasmKit/Execution/Runtime/Stack.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ struct Stack {
2323
returnPC: ProgramCounter,
2424
address: FunctionAddress? = nil
2525
) throws {
26-
// TODO: Stack overflow check can be done at the entry of expression
27-
guard (frames.count + numberOfValues) < limit else {
26+
guard frames.count < limit, (numberOfValues + iseq.maxStackHeight + (defaultLocals?.count ?? 0)) < limit else {
2827
throw Trap.callStackExhausted
2928
}
3029
let valueFrameIndex = self.numberOfValues - argc
@@ -63,6 +62,11 @@ struct Stack {
6362
self.valueStack.copyValues(copyCount: copyCount, popCount: popCount)
6463
}
6564

65+
func deallocate() {
66+
self.valueStack.deallocate()
67+
self.frames.deallocate()
68+
}
69+
6670
var topValue: Value {
6771
self.valueStack.topValue
6872
}
@@ -204,6 +208,10 @@ struct FixedSizeStack<Element> {
204208
subscript(_ index: Int) -> Element {
205209
self.buffer[index]
206210
}
211+
212+
func deallocate() {
213+
self.buffer.deallocate()
214+
}
207215
}
208216

209217
extension FixedSizeStack: Sequence {

Sources/WasmKit/ModuleParser.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,15 @@ func parseModule<Stream: ByteStream>(stream: Stream, features: WasmFeatureSet =
155155
features: features, hasDataCount: hasDataCount,
156156
visitor: &tracing
157157
)
158-
let newISeq = InstructionSequence(instructions: tracing.visitor.finalize())
158+
let newISeq = tracing.visitor.finalize()
159159
return newISeq
160160
}
161161
try WasmParser.parseExpression(
162162
bytes: Array(code.expression),
163163
features: features, hasDataCount: hasDataCount,
164164
visitor: &translator
165165
)
166-
return InstructionSequence(instructions: translator.finalize())
166+
return translator.finalize()
167167
})
168168
}
169169
module.functions = functions

Sources/WasmKit/Translator.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,16 @@ struct InstructionTranslator: InstructionVisitor {
145145
}
146146
struct ValueStack {
147147
private var values: [MetaValue] = []
148+
/// The maximum height of the stack within the function
149+
private(set) var maxHeight: Int = 0
148150
var height: Int { values.count }
149151

150152
mutating func push(_ value: ValueType) {
151-
self.values.append(.some(value))
153+
push(.some(value))
152154
}
153155
mutating func push(_ value: MetaValue) {
156+
// Record the maximum height of the stack we have seen
157+
maxHeight = max(maxHeight, height)
154158
self.values.append(value)
155159
}
156160

@@ -393,13 +397,14 @@ struct InstructionTranslator: InstructionVisitor {
393397
valueStack.truncate(height: currentFrame.stackHeight)
394398
}
395399

396-
public mutating func finalize() -> [Instruction] {
400+
public mutating func finalize() -> InstructionSequence {
397401
iseqBuilder.pinLabelHere(self.endOfFunctionLabel)
398402
#if DEBUG
399403
// Check dangling labels
400404
iseqBuilder.assertDanglingLabels()
401405
#endif
402-
return iseqBuilder.finalize()
406+
let instructions = iseqBuilder.finalize()
407+
return InstructionSequence(instructions: instructions, maxStackHeight: valueStack.maxHeight)
403408
}
404409

405410
// MARK: - Visitor

0 commit comments

Comments
 (0)