Skip to content

Commit fc9129b

Browse files
committed
runtime/jit: relocate caller frame pointers on stack growth
1 parent eb0f24e commit fc9129b

2 files changed

Lines changed: 56 additions & 24 deletions

File tree

src/runtime/stack.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,21 +1042,26 @@ func adjustUserFrame(u *unwinder, adjinfo *adjustinfo) {
10421042
if pc == 0 {
10431043
return
10441044
}
1045-
base, words, pointerMask, ok := userFramePointerMap(pc, sp)
1046-
if !ok || words == 0 {
1047-
return
1048-
}
1049-
bytes := words * goarch.PtrSize
10501045
newLo := adjinfo.old.lo + adjinfo.delta
10511046
newHi := adjinfo.old.hi + adjinfo.delta
1052-
if pointerMask == nil || bytes/goarch.PtrSize != words || base < newLo || base+bytes < base || base+bytes > newHi {
1053-
throw("invalid user frame pointer map")
1047+
if base, words, pointerMask, ok := userFramePointerMap(pc, sp); ok && words != 0 {
1048+
bytes := words * goarch.PtrSize
1049+
if pointerMask == nil || bytes/goarch.PtrSize != words || base < newLo || base+bytes < base || base+bytes > newHi {
1050+
throw("invalid user frame pointer map")
1051+
}
1052+
bits := bitvector{n: int32(words), bytedata: pointerMask}
1053+
if uintptr(bits.n) != words {
1054+
throw("user frame pointer map too large")
1055+
}
1056+
adjustpointers(unsafe.Pointer(base), &bits, adjinfo, funcInfo{})
10541057
}
1055-
bits := bitvector{n: int32(words), bytedata: pointerMask}
1056-
if uintptr(bits.n) != words {
1057-
throw("user frame pointer map too large")
1058+
1059+
if bpSlot, ok := userFrameCallerBPSlot(pc, sp); ok {
1060+
if bpSlot < newLo || bpSlot+goarch.PtrSize < bpSlot || bpSlot+goarch.PtrSize > newHi {
1061+
throw("invalid user frame caller frame pointer")
1062+
}
1063+
adjustpointer(adjinfo, unsafe.Pointer(bpSlot))
10581064
}
1059-
adjustpointers(unsafe.Pointer(base), &bits, adjinfo, funcInfo{})
10601065
}
10611066

10621067
// round x up to a power of 2.

src/runtime/userframe.go

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -250,21 +250,10 @@ func findUserFrameRegion(pc uintptr) *userFrameRegion {
250250

251251
func userFrameNext(r *userFrameRegion, pc, sp uintptr) (callerPC, callerSP, callerBP uintptr, ok bool) {
252252
if m := findUserFrameStackMap(r, pc); m != nil && m.hasUnwind {
253-
base := sp + m.unwindBaseOffset
254-
if base < sp {
253+
base, ok := userFrameUnwindBase(m, sp)
254+
if !ok {
255255
return 0, 0, 0, false
256256
}
257-
if m.unwindBaseUsesDelta {
258-
deltaAddr := sp + m.unwindBaseDeltaOffset
259-
if deltaAddr < sp {
260-
return 0, 0, 0, false
261-
}
262-
delta := *(*uintptr)(unsafe.Pointer(deltaAddr))
263-
if base+delta < base {
264-
return 0, 0, 0, false
265-
}
266-
base += delta
267-
}
268257
callerPCAddr := base + m.callerPCOffset
269258
callerSP = base + m.callerSPOffset
270259
callerBPAddr := base + m.callerBPOffset
@@ -276,6 +265,44 @@ func userFrameNext(r *userFrameRegion, pc, sp uintptr) (callerPC, callerSP, call
276265
return 0, 0, 0, false
277266
}
278267

268+
func userFrameUnwindBase(m *userFrameStackMap, sp uintptr) (uintptr, bool) {
269+
base := sp + m.unwindBaseOffset
270+
if base < sp {
271+
return 0, false
272+
}
273+
if m.unwindBaseUsesDelta {
274+
deltaAddr := sp + m.unwindBaseDeltaOffset
275+
if deltaAddr < sp {
276+
return 0, false
277+
}
278+
delta := *(*uintptr)(unsafe.Pointer(deltaAddr))
279+
if base+delta < base {
280+
return 0, false
281+
}
282+
base += delta
283+
}
284+
return base, true
285+
}
286+
287+
// userFrameCallerBPSlot returns the address of the saved caller frame pointer.
288+
// Stack copying must relocate this value even when the user frame has no Go
289+
// pointers of its own. On amd64, Go function epilogues may use LEAVE, so a
290+
// stale frame pointer would also restore SP to the old, freed stack.
291+
//
292+
//go:nosplit
293+
func userFrameCallerBPSlot(pc, sp uintptr) (uintptr, bool) {
294+
r := findUserFrameRegion(pc)
295+
m := findUserFrameStackMap(r, pc)
296+
if m == nil || !m.hasUnwind {
297+
return 0, false
298+
}
299+
base, ok := userFrameUnwindBase(m, sp)
300+
if !ok || base+m.callerBPOffset < base {
301+
return 0, false
302+
}
303+
return base + m.callerBPOffset, true
304+
}
305+
279306
// userFramePointerMap resolves one immutable map without calling user code.
280307
// Looking the region up again keeps unwinder pointer-free.
281308
//

0 commit comments

Comments
 (0)