Skip to content

Commit ee0a10e

Browse files
niaowdeadprogram
authored andcommitted
runtime (wasm): scan the system stack
This saves the system stack pointer into a global and uses it to scan. It should fix some use-after-free issues?
1 parent 39e0333 commit ee0a10e

File tree

3 files changed

+25
-11
lines changed

3 files changed

+25
-11
lines changed

src/internal/task/task_asyncify.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ func (*stackState) unwind()
108108
func (t *Task) Resume() {
109109
// The current task must be saved and restored because this can nest on WASM with JS.
110110
prevTask := currentTask
111+
if prevTask == nil {
112+
// Save the system stack pointer.
113+
saveStackPointer()
114+
}
111115
t.gcData.swap()
112116
currentTask = t
113117
if !t.state.launched {
@@ -123,6 +127,9 @@ func (t *Task) Resume() {
123127
}
124128
}
125129

130+
//go:linkname saveStackPointer runtime.saveStackPointer
131+
func saveStackPointer()
132+
126133
//export tinygo_rewind
127134
func (*state) rewind()
128135

src/runtime/arch_tinygowasm.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ func align(ptr uintptr) uintptr {
7373
//export tinygo_getCurrentStackPointer
7474
func getCurrentStackPointer() uintptr
7575

76+
// savedStackPointer is used to save the system stack pointer while in a goroutine.
77+
var savedStackPointer uintptr
78+
79+
// saveStackPointer is called by internal/task.state.Resume() to save the stack pointer before entering a goroutine from the system stack.
80+
func saveStackPointer() {
81+
savedStackPointer = getCurrentStackPointer()
82+
}
83+
7684
// growHeap tries to grow the heap size. It returns true if it succeeds, false
7785
// otherwise.
7886
func growHeap() bool {

src/runtime/gc_stack_portable.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,6 @@ type stackChainObject struct {
2929
// - The system stack (aka startup stack) is not heap allocated, so even
3030
// though it may be referenced it will not be scanned by default.
3131
//
32-
// Therefore, we only need to scan the system stack.
33-
// It is relatively easy to scan the system stack while we're on it: we can
34-
// simply read __stack_pointer and __global_base and scan the area in between.
35-
// Unfortunately, it's hard to get the system stack pointer while we're on a
36-
// goroutine stack. But when we're on a goroutine stack, the system stack is in
37-
// the scheduler which means there shouldn't be anything on the system stack
38-
// anyway.
39-
// ...I hope this assumption holds, otherwise we will need to store the system
40-
// stack in a global or something.
41-
//
4232
// The compiler also inserts code to store all globals in a chain via
4333
// stackChainStart. Luckily we don't need to scan these, as these globals are
4434
// stored on the goroutine stack and are therefore already getting scanned.
@@ -50,9 +40,18 @@ func markStack() {
5040
// live.
5141
volatile.LoadUint32((*uint32)(unsafe.Pointer(&stackChainStart)))
5242

43+
// Scan the system stack.
44+
var sysSP uintptr
5345
if task.OnSystemStack() {
54-
markRoots(getCurrentStackPointer(), stackTop)
46+
// We are on the system stack.
47+
// Use the current stack pointer.
48+
sysSP = getCurrentStackPointer()
49+
} else {
50+
// We are in a goroutine.
51+
// Use the saved stack pointer.
52+
sysSP = savedStackPointer
5553
}
54+
markRoots(sysSP, stackTop)
5655
}
5756

5857
// trackPointer is a stub function call inserted by the compiler during IR

0 commit comments

Comments
 (0)