Skip to content

Commit

Permalink
sys: add (un)maskProfilerSignal to disable SIGPROF
Browse files Browse the repository at this point in the history
sys.maskProfilerSignal() locks the calling goroutine to its underlying OS
thread and blocks the SIGPROF signal from interrupting the thread.

sys.unmaskProfilerSignal() reverses the operation and should be used in
tandem to properly restore OS thread state afterwards.

Add a wrapper around unix.PthreadSigmask added in a recent version of x/sys.

Signed-off-by: Timo Beckers <timo@isovalent.com>
  • Loading branch information
ti-mo committed Sep 28, 2022
1 parent b28d9b3 commit 1bdff75
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 2 deletions.
35 changes: 35 additions & 0 deletions internal/sys/signals.go
Expand Up @@ -2,11 +2,46 @@ package sys

import (
"fmt"
"runtime"
"unsafe"

"github.com/cilium/ebpf/internal/unix"
)

var profSet unix.Sigset_t

func init() {
if err := sigsetAdd(&profSet, unix.SIGPROF); err != nil {
panic(fmt.Errorf("creating signal set: %w", err))
}
}

// maskProfilerSignal locks the calling goroutine to its underlying OS thread
// and adds SIGPROF to the thread's signal mask. This prevents pprof from
// interrupting expensive syscalls like e.g. BPF_PROG_LOAD.
//
// The caller must defer sys.UnmaskProfilerSignal() to reverse the operation.
func maskProfilerSignal() {
runtime.LockOSThread()

if err := unix.PthreadSigmask(unix.SIG_BLOCK, &profSet, nil); err != nil {
runtime.UnlockOSThread()
panic(fmt.Errorf("masking profiler signal: %w", err))
}
}

// unmaskProfilerSignal removes SIGPROF from the underlying thread's signal
// mask, allowing it to be interrupted for profiling once again.
//
// It also unlocks the current goroutine from its underlying OS thread.
func unmaskProfilerSignal() {
defer runtime.UnlockOSThread()

if err := unix.PthreadSigmask(unix.SIG_UNBLOCK, &profSet, nil); err != nil {
panic(fmt.Errorf("unmasking profiler signal: %w", err))
}
}

const (
wordBytes = int(unsafe.Sizeof(unix.Sigset_t{}.Val[0]))
wordBits = wordBytes * 8
Expand Down
21 changes: 21 additions & 0 deletions internal/sys/signals_test.go
@@ -1,6 +1,7 @@
package sys

import (
"runtime"
"testing"

"github.com/cilium/ebpf/internal/unix"
Expand Down Expand Up @@ -38,3 +39,23 @@ func TestSigset(t *testing.T) {
t.Fatal("expected negative signal to be rejected")
}
}

func TestProfilerSignal(t *testing.T) {
// Additional goroutine lock to make the PthreadSigmask below execute on the
// same OS thread as the functions under test. UnlockOSThread needs to be
// called as many times as LockOSThread to unlock the goroutine.
runtime.LockOSThread()
defer runtime.UnlockOSThread()

maskProfilerSignal()
unmaskProfilerSignal()

var old unix.Sigset_t
if err := unix.PthreadSigmask(0, nil, &old); err != nil {
t.Fatal("getting old sigmask:", err)
}
var want unix.Sigset_t
if old != want {
t.Fatal("unmask operation didn't result in empty signal mask")
}
}
8 changes: 8 additions & 0 deletions internal/unix/signals_generic.go
@@ -0,0 +1,8 @@
//go:build linux && !mips && !mipsle && !mips64 && !mips64le

package unix

const (
SIG_BLOCK = 0
SIG_UNBLOCK = 1
)
8 changes: 8 additions & 0 deletions internal/unix/signals_mips.go
@@ -0,0 +1,8 @@
//go:build linux && (mips || mipsle || mips64 || mips64le)

package unix

const (
SIG_BLOCK = 1
SIG_UNBLOCK = 2
)
7 changes: 6 additions & 1 deletion internal/unix/types_linux.go
@@ -1,5 +1,4 @@
//go:build linux
// +build linux

package unix

Expand Down Expand Up @@ -70,6 +69,7 @@ const (
SO_ATTACH_BPF = linux.SO_ATTACH_BPF
SO_DETACH_BPF = linux.SO_DETACH_BPF
SOL_SOCKET = linux.SOL_SOCKET
SIGPROF = linux.SIGPROF
)

type Statfs_t = linux.Statfs_t
Expand All @@ -83,6 +83,11 @@ func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
return linux.Syscall(trap, a1, a2, a3)
}

// PthreadSigmask is a wrapper
func PthreadSigmask(how int, set, oldset *Sigset_t) error {
return linux.PthreadSigmask(how, set, oldset)
}

// FcntlInt is a wrapper
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
return linux.FcntlInt(fd, cmd, arg)
Expand Down
10 changes: 9 additions & 1 deletion internal/unix/types_other.go
@@ -1,5 +1,4 @@
//go:build !linux
// +build !linux

package unix

Expand Down Expand Up @@ -72,6 +71,10 @@ const (
SO_ATTACH_BPF = 0x32
SO_DETACH_BPF = 0x1b
SOL_SOCKET = 0x1
SIGPROF = 0

SIG_BLOCK = 0
SIG_UNBLOCK = 0
)

type Statfs_t struct {
Expand Down Expand Up @@ -107,6 +110,11 @@ func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
return 0, 0, syscall.Errno(1)
}

// PthreadSigmask is a wrapper
func PthreadSigmask(how int, set, oldset *Sigset_t) error {
return errNonLinux
}

// FcntlInt is a wrapper
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
return -1, errNonLinux
Expand Down

0 comments on commit 1bdff75

Please sign in to comment.