Skip to content

Commit 9aeee0f

Browse files
committed
runtime: fix infinite loop in lockextra on linux/arm
This commit fixes issue #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.
1 parent d5cfcc7 commit 9aeee0f

File tree

3 files changed

+97
-13
lines changed

3 files changed

+97
-13
lines changed

src/runtime/signal_unix.go

+65-13
Original file line numberDiff line numberDiff line change
@@ -274,19 +274,56 @@ func sigpipe() {
274274
dieFromSignal(_SIGPIPE)
275275
}
276276

277-
// sigFetchG fetches the value of G safely when running in a signal handler.
278277
// On some architectures, the g value may be clobbered when running in a VDSO.
279278
// See issue #32912.
280279
//
281280
//go:nosplit
282-
func sigFetchG(c *sigctxt) *g {
281+
func sigClobbered(c *sigctxt) bool {
283282
switch GOARCH {
284283
case "arm", "arm64":
285-
if inVDSOPage(c.sigpc()) {
286-
return nil
284+
return inVDSOPage(c.sigpc());
285+
}
286+
return false
287+
}
288+
289+
// sigpending stores signals during the Go signal handler when the g value is clobbered.
290+
// See issue #34391.
291+
var sigpending [(_NSIG + 31) / 32]uint32
292+
293+
func sigAddPending(s uint32) {
294+
for {
295+
p := sigpending[s/32]
296+
q := p | (1 << (s & 31))
297+
if atomic.Cas(&sigpending[s/32], p, q) {
298+
return
287299
}
288300
}
289-
return getg()
301+
}
302+
303+
// sigClearPending is called from outside the signal handler context.
304+
// It should be called just after the clobbered G value is restored.
305+
//go:nosplit
306+
//go:nowritebarrierrec
307+
func sigClearPending() {
308+
for s := 0; s < _NSIG; s++ {
309+
// steal signal from pending queue
310+
steal := false
311+
for {
312+
p := sigpending[s/32]
313+
if p & (1 << (s & 31)) == 0 {
314+
break
315+
}
316+
q := p &^ (1 << (s & 31))
317+
if atomic.Cas(&sigpending[s/32], p, q) {
318+
steal = true
319+
break
320+
}
321+
}
322+
if !steal {
323+
continue
324+
}
325+
raise(uint32(s))
326+
}
290327
}
291328

292329
// sigtrampgo is called from the signal handler function, sigtramp,
@@ -305,7 +342,16 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
305342
return
306343
}
307344
c := &sigctxt{info, ctx}
308-
g := sigFetchG(c)
345+
if sigClobbered(c) {
346+
if sig == _SIGPROF {
347+
sigprofNonGoPC(c.sigpc())
348+
return
349+
}
350+
// at this point iscgo must be true
351+
sigAddPending(sig)
352+
return
353+
}
354+
g := getg()
309355
if g == nil {
310356
if sig == _SIGPROF {
311357
sigprofNonGoPC(c.sigpc())
@@ -670,13 +716,19 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
670716
if (c.sigcode() == _SI_USER || flags&_SigPanic == 0) && sig != _SIGPIPE {
671717
return false
672718
}
673-
// Determine if the signal occurred inside Go code. We test that:
674-
// (1) we weren't in VDSO page,
675-
// (2) we were in a goroutine (i.e., m.curg != nil), and
676-
// (3) we weren't in CGO.
677-
g := sigFetchG(c)
678-
if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo {
679-
return false
719+
if sigClobbered(c) {
720+
// There is no handler to be forwarded to.
721+
if !iscgo {
722+
return false
723+
}
724+
} else {
725+
// Determine if the signal occurred inside Go code. We test that:
726+
// (1) we were in a goroutine (i.e., m.curg != nil), and
727+
// (2) we weren't in CGO.
728+
g := getg()
729+
if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo {
730+
return false
731+
}
680732
}
681733

682734
// Signal not handled by Go, forward it.

src/runtime/sys_linux_arm.s

+18
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,15 @@ noswitch:
247247
B.EQ fallback
248248

249249
BL (R11)
250+
251+
SUB $24, R13
252+
MOVW R4, 8(R13)
253+
MOVW R5, 12(R13)
254+
BL runtime·sigClearPending(SB)
255+
MOVW 12(R13), R5
256+
MOVW 8(R13), R4
257+
ADD $24, R13
258+
250259
JMP finish
251260

252261
fallback:
@@ -298,6 +307,15 @@ noswitch:
298307
B.EQ fallback
299308

300309
BL (R11)
310+
311+
SUB $24, R13
312+
MOVW R4, 8(R13)
313+
MOVW R5, 12(R13)
314+
BL runtime·sigClearPending(SB)
315+
MOVW 12(R13), R5
316+
MOVW 8(R13), R4
317+
ADD $24, R13
318+
301319
JMP finish
302320

303321
fallback:

src/runtime/sys_linux_arm64.s

+14
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,13 @@ noswitch:
208208
MOVD runtime·vdsoClockgettimeSym(SB), R2
209209
CBZ R2, fallback
210210
BL (R2)
211+
212+
SUB $24, RSP
213+
MOVD R20, 16(RSP)
214+
BL runtime·sigClearPending(SB)
215+
MOVD 16(RSP), R20
216+
ADD $24, RSP
217+
211218
B finish
212219

213220
fallback:
@@ -251,6 +258,13 @@ noswitch:
251258
MOVD runtime·vdsoClockgettimeSym(SB), R2
252259
CBZ R2, fallback
253260
BL (R2)
261+
262+
SUB $24, RSP
263+
MOVD R20, 16(RSP)
264+
BL runtime·sigClearPending(SB)
265+
MOVD 16(RSP), R20
266+
ADD $24, RSP
267+
254268
B finish
255269

256270
fallback:

0 commit comments

Comments
 (0)