Skip to content

Commit 675eb72

Browse files
committed
runtime: run libc SIGSETXID and SIGCANCEL handlers on signal stack
These signals are used by glibc to broadcast setuid/setgid to all threads and to send pthread cancellations. Unlike other signals, the Go runtime does not intercept these because they must invoke the libc handlers (see issues #3871 and #6997). However, because 1) these signals may be issued asynchronously by a thread running C code to another thread running Go code and 2) glibc does not set SA_ONSTACK for its handlers, glibc's signal handler may be run on a Go stack. Signal frames range from 1.5K on amd64 to many kilobytes on ppc64, so this may overflow the Go stack and corrupt heap (or other stack) data. Fix this by ensuring that these signal handlers have the SA_ONSTACK flag (but not otherwise taking over the handler). This has been a problem since Go 1.1, but it's likely that people haven't encountered it because it only affects setuid/setgid and pthread_cancel. Fixes #9600. Change-Id: I6cf5f5c2d3aa48998d632f61f1ddc2778dcfd300 Reviewed-on: https://go-review.googlesource.com/1887 Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 3ffc975 commit 675eb72

17 files changed

+217
-2
lines changed

misc/cgo/test/cgo_linux_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ import "testing"
99
func TestSetgid(t *testing.T) { testSetgid(t) }
1010
func Test6997(t *testing.T) { test6997(t) }
1111
func TestBuildID(t *testing.T) { testBuildID(t) }
12+
func Test9400(t *testing.T) { test9400(t) }

misc/cgo/test/issue9400/asm_386.s

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include "textflag.h"
2+
3+
TEXT ·RewindAndSetgid(SB),NOSPLIT,$0-0
4+
// Rewind stack pointer so anything that happens on the stack
5+
// will clobber the test pattern created by the caller
6+
ADDL $(1024 * 8), SP
7+
8+
// Ask signaller to setgid
9+
MOVL $1, ·Baton(SB)
10+
11+
// Wait for setgid completion
12+
loop:
13+
PAUSE
14+
MOVL ·Baton(SB), AX
15+
CMPL AX, $0
16+
JNE loop
17+
18+
// Restore stack
19+
SUBL $(1024 * 8), SP
20+
RET

misc/cgo/test/issue9400/asm_amd64x.s

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// +build amd64 amd64p32
2+
3+
#include "textflag.h"
4+
5+
TEXT ·RewindAndSetgid(SB),NOSPLIT,$0-0
6+
// Rewind stack pointer so anything that happens on the stack
7+
// will clobber the test pattern created by the caller
8+
ADDQ $(1024 * 8), SP
9+
10+
// Ask signaller to setgid
11+
MOVL $1, ·Baton(SB)
12+
13+
// Wait for setgid completion
14+
loop:
15+
PAUSE
16+
MOVL ·Baton(SB), AX
17+
CMPL AX, $0
18+
JNE loop
19+
20+
// Restore stack
21+
SUBQ $(1024 * 8), SP
22+
RET

misc/cgo/test/issue9400/asm_arm.s

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include "textflag.h"
2+
3+
TEXT cas<>(SB),NOSPLIT,$0
4+
MOVW $0xffff0fc0, PC
5+
6+
TEXT ·RewindAndSetgid(SB),NOSPLIT,$-4-0
7+
// Save link register
8+
MOVW R14, R4
9+
10+
// Rewind stack pointer so anything that happens on the stack
11+
// will clobber the test pattern created by the caller
12+
ADD $(1024 * 8), R13
13+
14+
// Ask signaller to setgid
15+
MOVW $·Baton(SB), R2
16+
storeloop:
17+
MOVW 0(R2), R0
18+
MOVW $1, R1
19+
BL cas<>(SB)
20+
BCC storeloop
21+
22+
// Wait for setgid completion
23+
loop:
24+
MOVW $0, R0
25+
MOVW $0, R1
26+
BL cas<>(SB)
27+
BCC loop
28+
29+
// Restore stack
30+
SUB $(1024 * 8), R13
31+
32+
MOVW R4, R14
33+
RET

misc/cgo/test/issue9400/asm_ppc64x.s

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// +build ppc64 ppc64le
2+
3+
#include "textflag.h"
4+
5+
TEXT ·RewindAndSetgid(SB),NOSPLIT,$-8-0
6+
// Rewind stack pointer so anything that happens on the stack
7+
// will clobber the test pattern created by the caller
8+
ADD $(1024 * 8), R1
9+
10+
// Ask signaller to setgid
11+
MOVW $1, R3
12+
SYNC
13+
MOVW R3, ·Baton(SB)
14+
15+
// Wait for setgid completion
16+
loop:
17+
SYNC
18+
MOVW ·Baton(SB), R3
19+
CMP R3, $0
20+
// Hint that we're in a spin loop
21+
OR R1, R1, R1
22+
BNE loop
23+
ISYNC
24+
25+
// Restore stack
26+
SUB $(1024 * 8), R1
27+
RET

misc/cgo/test/issue9400/stubs.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright 2014 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package issue9400
6+
7+
var Baton int32
8+
9+
func RewindAndSetgid()

misc/cgo/test/issue9400_linux.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2014 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Test that SIGSETXID runs on signal stack, since it's likely to
6+
// overflow if it runs on the Go stack.
7+
8+
package cgotest
9+
10+
/*
11+
#include <sys/types.h>
12+
#include <unistd.h>
13+
*/
14+
import "C"
15+
16+
import (
17+
"runtime"
18+
"sync/atomic"
19+
"testing"
20+
21+
"./issue9400"
22+
)
23+
24+
func test9400(t *testing.T) {
25+
// We synchronize through a shared variable, so we need two procs
26+
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
27+
28+
// Start signaller
29+
atomic.StoreInt32(&issue9400.Baton, 0)
30+
go func() {
31+
// Wait for RewindAndSetgid
32+
for atomic.LoadInt32(&issue9400.Baton) == 0 {
33+
runtime.Gosched()
34+
}
35+
// Broadcast SIGSETXID
36+
runtime.LockOSThread()
37+
C.setgid(0)
38+
// Indicate that signalling is done
39+
atomic.StoreInt32(&issue9400.Baton, 0)
40+
}()
41+
42+
// Grow the stack and put down a test pattern
43+
const pattern = 0x123456789abcdef
44+
var big [1024]uint64 // len must match assmebly
45+
for i := range big {
46+
big[i] = pattern
47+
}
48+
49+
// Temporarily rewind the stack and trigger SIGSETXID
50+
issue9400.RewindAndSetgid()
51+
52+
// Check test pattern
53+
for i := range big {
54+
if big[i] != pattern {
55+
t.Fatalf("entry %d of test pattern is wrong; %#x != %#x", i, big[i], pattern)
56+
}
57+
}
58+
}

src/runtime/os1_darwin.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ func setsig(i int32, fn uintptr, restart bool) {
394394
sigaction(uint32(i), &sa, nil)
395395
}
396396

397+
func setsigstack(i int32) {
398+
gothrow("setsigstack")
399+
}
400+
397401
func getsig(i int32) uintptr {
398402
var sa sigactiont
399403
memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa))

src/runtime/os1_dragonfly.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ func setsig(i int32, fn uintptr, restart bool) {
189189
sigaction(i, &sa, nil)
190190
}
191191

192+
func setsigstack(i int32) {
193+
gothrow("setsigstack")
194+
}
195+
192196
func getsig(i int32) uintptr {
193197
var sa sigactiont
194198
sigaction(i, nil, &sa)

src/runtime/os1_freebsd.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,11 @@ func setsig(i int32, fn uintptr, restart bool) {
190190
sa.sa_handler = fn
191191
sigaction(i, &sa, nil)
192192
}
193+
194+
func setsigstack(i int32) {
195+
gothrow("setsigstack")
196+
}
197+
193198
func getsig(i int32) uintptr {
194199
var sa sigactiont
195200
sigaction(i, nil, &sa)

src/runtime/os1_linux.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,20 @@ func setsig(i int32, fn uintptr, restart bool) {
246246
}
247247
}
248248

249+
func setsigstack(i int32) {
250+
var sa sigactiont
251+
if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 {
252+
gothrow("rt_sigaction failure")
253+
}
254+
if sa.sa_handler == 0 || sa.sa_handler == _SIG_DFL || sa.sa_handler == _SIG_IGN || sa.sa_flags&_SA_ONSTACK != 0 {
255+
return
256+
}
257+
sa.sa_flags |= _SA_ONSTACK
258+
if rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) != 0 {
259+
gothrow("rt_sigaction failure")
260+
}
261+
}
262+
249263
func getsig(i int32) uintptr {
250264
var sa sigactiont
251265

src/runtime/os1_netbsd.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,10 @@ func setsig(i int32, fn uintptr, restart bool) {
233233
sigaction(i, &sa, nil)
234234
}
235235

236+
func setsigstack(i int32) {
237+
gothrow("setsigstack")
238+
}
239+
236240
func getsig(i int32) uintptr {
237241
var sa sigactiont
238242
sigaction(i, nil, &sa)

src/runtime/os1_openbsd.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ func setsig(i int32, fn uintptr, restart bool) {
203203
sigaction(i, &sa, nil)
204204
}
205205

206+
func setsigstack(i int32) {
207+
gothrow("setsigstack")
208+
}
209+
206210
func getsig(i int32) uintptr {
207211
var sa sigactiont
208212
sigaction(i, nil, &sa)

src/runtime/os3_solaris.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ func setsig(i int32, fn uintptr, restart bool) {
252252
sigaction(i, &sa, nil)
253253
}
254254

255+
func setsigstack(i int32) {
256+
gothrow("setsigstack")
257+
}
258+
255259
func getsig(i int32) uintptr {
256260
var sa sigactiont
257261
sigaction(i, nil, &sa)

src/runtime/runtime2.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ const (
372372
_SigHandling = 1 << 5 // our signal handler is registered
373373
_SigIgnored = 1 << 6 // the signal was ignored before we registered for it
374374
_SigGoExit = 1 << 7 // cause all runtime procs to exit (only used on Plan 9).
375+
_SigSetStack = 1 << 8 // add SA_ONSTACK to libc handler
375376
)
376377

377378
// Layout of in-memory per-function information prepared by linker

src/runtime/signal1_unix.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ func initsig() {
3737
}
3838
}
3939

40+
if t.flags&_SigSetStack != 0 {
41+
setsigstack(i)
42+
continue
43+
}
44+
4045
t.flags |= _SigHandling
4146
setsig(i, funcPC(sighandler), true)
4247
}

src/runtime/signal_linux.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ var sigtable = [...]sigTabT{
4242
/* 29 */ {_SigNotify, "SIGIO: i/o now possible"},
4343
/* 30 */ {_SigNotify, "SIGPWR: power failure restart"},
4444
/* 31 */ {_SigNotify, "SIGSYS: bad system call"},
45-
/* 32 */ {0, "signal 32"}, /* SIGCANCEL; see issue 6997 */
46-
/* 33 */ {0, "signal 33"}, /* SIGSETXID; see issue 3871 */
45+
/* 32 */ {_SigSetStack, "signal 32"}, /* SIGCANCEL; see issue 6997 */
46+
/* 33 */ {_SigSetStack, "signal 33"}, /* SIGSETXID; see issue 3871, 9400 */
4747
/* 34 */ {_SigNotify, "signal 34"},
4848
/* 35 */ {_SigNotify, "signal 35"},
4949
/* 36 */ {_SigNotify, "signal 36"},

0 commit comments

Comments
 (0)