@@ -35,11 +35,16 @@ type state struct {
3535type stackState struct {
3636 // asyncify is the stack pointer of the asyncify stack.
3737 // This starts from the bottom and grows upwards.
38- asyncifysp uintptr
38+ asyncifysp unsafe. Pointer
3939
4040 // asyncify is stack pointer of the C stack.
4141 // This starts from the top and grows downwards.
42- csp uintptr
42+ csp unsafe.Pointer
43+
44+ // Pointer to the first (lowest address) of the stack. It must never be
45+ // overwritten. It can be checked from time to time to see whether a stack
46+ // overflow happened in the past.
47+ canaryPtr * uintptr
4348}
4449
4550// start creates and starts a new goroutine with the given function and arguments.
@@ -63,12 +68,18 @@ func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
6368 s .args = args
6469
6570 // Create a stack.
66- stack := make ([]uintptr , stackSize / unsafe .Sizeof (uintptr (0 )))
71+ stack := runtime_alloc (stackSize , nil )
72+
73+ // Set up the stack canary, a random number that should be checked when
74+ // switching from the task back to the scheduler. The stack canary pointer
75+ // points to the first word of the stack. If it has changed between now and
76+ // the next stack switch, there was a stack overflow.
77+ s .canaryPtr = (* uintptr )(stack )
78+ * s .canaryPtr = stackCanary
6779
6880 // Calculate stack base addresses.
69- s .asyncifysp = uintptr (unsafe .Pointer (& stack [0 ]))
70- s .csp = uintptr (unsafe .Pointer (& stack [0 ])) + uintptr (len (stack ))* unsafe .Sizeof (uintptr (0 ))
71- stack [0 ] = stackCanary
81+ s .asyncifysp = unsafe .Add (stack , unsafe .Sizeof (uintptr (0 )))
82+ s .csp = unsafe .Add (stack , stackSize )
7283}
7384
7485//go:linkname runqueuePushBack runtime.runqueuePushBack
@@ -85,14 +96,11 @@ func Current() *Task {
8596// Pause suspends the current task and returns to the scheduler.
8697// This function may only be called when running on a goroutine stack, not when running on the system stack.
8798func Pause () {
88- // This is mildly unsafe but this is also the only place we can do this.
89- if * (* uintptr )(unsafe .Pointer (currentTask .state .asyncifysp )) != stackCanary {
99+ if * currentTask .state .canaryPtr != stackCanary {
90100 runtimePanic ("stack overflow" )
91101 }
92102
93103 currentTask .state .unwind ()
94-
95- * (* uintptr )(unsafe .Pointer (currentTask .state .asyncifysp )) = stackCanary
96104}
97105
98106//export tinygo_unwind
@@ -113,7 +121,7 @@ func (t *Task) Resume() {
113121 }
114122 currentTask = prevTask
115123 t .gcData .swap ()
116- if t .state .asyncifysp > t .state .csp {
124+ if uintptr ( t .state .asyncifysp ) > uintptr ( t .state .csp ) {
117125 runtimePanic ("stack overflow" )
118126 }
119127}
0 commit comments