1// Copyright 2016 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// Support for memory sanitizer. See runtime/cgo/sigaction.go.
6
7// +build linux,amd64 freebsd,amd64 linux,arm64
8
9package runtime
10
11import "unsafe"
12
13// _cgo_sigaction is filled in by runtime/cgo when it is linked into the
14// program, so it is only non-nil when using cgo.
15//go:linkname _cgo_sigaction _cgo_sigaction
16var _cgo_sigaction unsafe.Pointer
17
18//go:nosplit
19//go:nowritebarrierrec
20func sigaction(sig uint32, new, old *sigactiont) {
21	// The runtime package is explicitly blacklisted from sanitizer
22	// instrumentation in racewalk.go, but we might be calling into instrumented C
23	// functions here — so we need the pointer parameters to be properly marked.
24	//
25	// Mark the input as having been written before the call and the output as
26	// read after.
27	if msanenabled && new != nil {
28		msanwrite(unsafe.Pointer(new), unsafe.Sizeof(*new))
29	}
30
31	if _cgo_sigaction == nil || inForkedChild {
32		sysSigaction(sig, new, old)
33	} else {
34		// We need to call _cgo_sigaction, which means we need a big enough stack
35		// for C.  To complicate matters, we may be in libpreinit (before the
36		// runtime has been initialized) or in an asynchronous signal handler (with
37		// the current thread in transition between goroutines, or with the g0
38		// system stack already in use).
39
40		var ret int32
41
42		var g *g
43		if mainStarted {
44			g = getg()
45		}
46		sp := uintptr(unsafe.Pointer(&sig))
47		switch {
48		case g == nil:
49			// No g: we're on a C stack or a signal stack.
50			ret = callCgoSigaction(uintptr(sig), new, old)
51		case sp < g.stack.lo || sp >= g.stack.hi:
52			// We're no longer on g's stack, so we must be handling a signal.  It's
53			// possible that we interrupted the thread during a transition between g
54			// and g0, so we should stay on the current stack to avoid corrupting g0.
55			ret = callCgoSigaction(uintptr(sig), new, old)
56		default:
57			// We're running on g's stack, so either we're not in a signal handler or
58			// the signal handler has set the correct g.  If we're on gsignal or g0,
59			// systemstack will make the call directly; otherwise, it will switch to
60			// g0 to ensure we have enough room to call a libc function.
61			//
62			// The function literal that we pass to systemstack is not nosplit, but
63			// that's ok: we'll be running on a fresh, clean system stack so the stack
64			// check will always succeed anyway.
65			systemstack(func() {
66				ret = callCgoSigaction(uintptr(sig), new, old)
67			})
68		}
69
70		const EINVAL = 22
71		if ret == EINVAL {
72			// libc reserves certain signals — normally 32-33 — for pthreads, and
73			// returns EINVAL for sigaction calls on those signals.  If we get EINVAL,
74			// fall back to making the syscall directly.
75			sysSigaction(sig, new, old)
76		}
77	}
78
79	if msanenabled && old != nil {
80		msanread(unsafe.Pointer(old), unsafe.Sizeof(*old))
81	}
82}
83
84// callCgoSigaction calls the sigaction function in the runtime/cgo package
85// using the GCC calling convention. It is implemented in assembly.
86//go:noescape
87func callCgoSigaction(sig uintptr, new, old *sigactiont) int32
88