Skip to content

Commit

Permalink
runtime: fix infinite loop in lockextra on linux/arm
Browse files Browse the repository at this point in the history
This commit fixes issue golang#34391, which is due to an incorrect patch
merged in CL 192937.

sigtrampgo is modified to record incoming signals in a globally shared
atomic bitmask when the G register is clobbered. When the execution
exits from vdso it checks if there are pending signals and in that
case it re-raises them to its own process.
  • Loading branch information
nyuichi committed Oct 18, 2019
1 parent d5cfcc7 commit 9aeee0f
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 13 deletions.
78 changes: 65 additions & 13 deletions src/runtime/signal_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,19 +274,56 @@ func sigpipe() {
dieFromSignal(_SIGPIPE)
}

// sigFetchG fetches the value of G safely when running in a signal handler.
// On some architectures, the g value may be clobbered when running in a VDSO.
// See issue #32912.
//
//go:nosplit
func sigFetchG(c *sigctxt) *g {
func sigClobbered(c *sigctxt) bool {
switch GOARCH {
case "arm", "arm64":
if inVDSOPage(c.sigpc()) {
return nil
return inVDSOPage(c.sigpc());
}
return false
}

// sigpending stores signals during the Go signal handler when the g value is clobbered.
// See issue #34391.
var sigpending [(_NSIG + 31) / 32]uint32

func sigAddPending(s uint32) {
for {
p := sigpending[s/32]
q := p | (1 << (s & 31))
if atomic.Cas(&sigpending[s/32], p, q) {
return
}
}
return getg()
}

// sigClearPending is called from outside the signal handler context.
// It should be called just after the clobbered G value is restored.
//go:nosplit
//go:nowritebarrierrec
func sigClearPending() {
for s := 0; s < _NSIG; s++ {
// steal signal from pending queue
steal := false
for {
p := sigpending[s/32]
if p & (1 << (s & 31)) == 0 {
break
}
q := p &^ (1 << (s & 31))
if atomic.Cas(&sigpending[s/32], p, q) {
steal = true
break
}
}
if !steal {
continue
}
raise(uint32(s))
}
}

// sigtrampgo is called from the signal handler function, sigtramp,
Expand All @@ -305,7 +342,16 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
return
}
c := &sigctxt{info, ctx}
g := sigFetchG(c)
if sigClobbered(c) {
if sig == _SIGPROF {
sigprofNonGoPC(c.sigpc())
return
}
// at this point iscgo must be true
sigAddPending(sig)
return
}
g := getg()
if g == nil {
if sig == _SIGPROF {
sigprofNonGoPC(c.sigpc())
Expand Down Expand Up @@ -670,13 +716,19 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
if (c.sigcode() == _SI_USER || flags&_SigPanic == 0) && sig != _SIGPIPE {
return false
}
// Determine if the signal occurred inside Go code. We test that:
// (1) we weren't in VDSO page,
// (2) we were in a goroutine (i.e., m.curg != nil), and
// (3) we weren't in CGO.
g := sigFetchG(c)
if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo {
return false
if sigClobbered(c) {
// There is no handler to be forwarded to.
if !iscgo {
return false
}
} else {
// Determine if the signal occurred inside Go code. We test that:
// (1) we were in a goroutine (i.e., m.curg != nil), and
// (2) we weren't in CGO.
g := getg()
if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo {
return false
}
}

// Signal not handled by Go, forward it.
Expand Down
18 changes: 18 additions & 0 deletions src/runtime/sys_linux_arm.s
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,15 @@ noswitch:
B.EQ fallback

BL (R11)

SUB $24, R13
MOVW R4, 8(R13)
MOVW R5, 12(R13)
BL runtime·sigClearPending(SB)
MOVW 12(R13), R5
MOVW 8(R13), R4
ADD $24, R13

JMP finish

fallback:
Expand Down Expand Up @@ -298,6 +307,15 @@ noswitch:
B.EQ fallback

BL (R11)

SUB $24, R13
MOVW R4, 8(R13)
MOVW R5, 12(R13)
BL runtime·sigClearPending(SB)
MOVW 12(R13), R5
MOVW 8(R13), R4
ADD $24, R13

JMP finish

fallback:
Expand Down
14 changes: 14 additions & 0 deletions src/runtime/sys_linux_arm64.s
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ noswitch:
MOVD runtime·vdsoClockgettimeSym(SB), R2
CBZ R2, fallback
BL (R2)

SUB $24, RSP
MOVD R20, 16(RSP)
BL runtime·sigClearPending(SB)
MOVD 16(RSP), R20
ADD $24, RSP

B finish

fallback:
Expand Down Expand Up @@ -251,6 +258,13 @@ noswitch:
MOVD runtime·vdsoClockgettimeSym(SB), R2
CBZ R2, fallback
BL (R2)

SUB $24, RSP
MOVD R20, 16(RSP)
BL runtime·sigClearPending(SB)
MOVD 16(RSP), R20
ADD $24, RSP

B finish

fallback:
Expand Down

0 comments on commit 9aeee0f

Please sign in to comment.