diff options
Diffstat (limited to 'src/runtime/cgocall.go')
-rw-r--r-- | src/runtime/cgocall.go | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index 1da7249abc..d226c2e907 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -206,6 +206,73 @@ func cgocall(fn, arg unsafe.Pointer) int32 { return errno } +// Set or reset the system stack bounds for a callback on sp. +// +// Must be nosplit because it is called by needm prior to fully initializing +// the M. +// +//go:nosplit +func callbackUpdateSystemStack(mp *m, sp uintptr, signal bool) { + g0 := mp.g0 + if sp > g0.stack.lo && sp <= g0.stack.hi { + // Stack already in bounds, nothing to do. + return + } + + if mp.ncgo > 0 { + // ncgo > 0 indicates that this M was in Go further up the stack + // (it called C and is now receiving a callback). It is not + // safe for the C call to change the stack out from under us. + + // Note that this case isn't possible for signal == true, as + // that is always passing a new M from needm. + + // Stack is bogus, but reset the bounds anyway so we can print. + hi := g0.stack.hi + lo := g0.stack.lo + g0.stack.hi = sp + 1024 + g0.stack.lo = sp - 32*1024 + g0.stackguard0 = g0.stack.lo + stackGuard + + print("M ", mp.id, " procid ", mp.procid, " runtime: cgocallback with sp=", hex(sp), " out of bounds [", hex(lo), ", ", hex(hi), "]") + print("\n") + exit(2) + } + + // This M does not have Go further up the stack. However, it may have + // previously called into Go, initializing the stack bounds. Between + // that call returning and now the stack may have changed (perhaps the + // C thread is running a coroutine library). We need to update the + // stack bounds for this case. + // + // Set the stack bounds to match the current stack. If we don't + // actually know how big the stack is, like we don't know how big any + // scheduling stack is, but we assume there's at least 32 kB. If we + // can get a more accurate stack bound from pthread, use that, provided + // it actually contains SP.. + g0.stack.hi = sp + 1024 + g0.stack.lo = sp - 32*1024 + if !signal && _cgo_getstackbound != nil { + // Don't adjust if called from the signal handler. + // We are on the signal stack, not the pthread stack. + // (We could get the stack bounds from sigaltstack, but + // we're getting out of the signal handler very soon + // anyway. Not worth it.) + var bounds [2]uintptr + asmcgocall(_cgo_getstackbound, unsafe.Pointer(&bounds)) + // getstackbound is an unsupported no-op on Windows. + // + // Don't use these bounds if they don't contain SP. Perhaps we + // were called by something not using the standard thread + // stack. + if bounds[0] != 0 && sp > bounds[0] && sp <= bounds[1] { + g0.stack.lo = bounds[0] + g0.stack.hi = bounds[1] + } + } + g0.stackguard0 = g0.stack.lo + stackGuard +} + // Call from C back to Go. fn must point to an ABIInternal Go entry-point. // //go:nosplit @@ -216,6 +283,9 @@ func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) { exit(2) } + sp := gp.m.g0.sched.sp // system sp saved by cgocallback. + callbackUpdateSystemStack(gp.m, sp, false) + // The call from C is on gp.m's g0 stack, so we must ensure // that we stay on that M. We have to do this before calling // exitsyscall, since it would otherwise be free to move us to |