platform/kvm: rewriting bluepill() without signal handler trampolining

Implement bluepill() without relying on a signal handler trampoline by
directly switching stacks and execution contexts. Here, it is implemented
just for x86_64. The arm64 part will be implemented separately.

Here are benchmark results before and after this change: Before:
BenchmarkWorldSwitchToUserRoundtrip-8   	  255940	      4407 ns/op
After:
BenchmarkWorldSwitchToUserRoundtrip-8   	  307773	      3765 ns/op

Suggested-by: Jamie Liu <jamieliu@google.com>
PiperOrigin-RevId: 664989586
This commit is contained in:
Andrei Vagin
2024-08-19 14:50:36 -07:00
committed by gVisor bot
parent 043ce9c5d2
commit 374a11a7cd
17 changed files with 427 additions and 75 deletions
+2
View File
@@ -106,6 +106,7 @@ analyzers:
- "\\[amd64\\].*sysret: RET without writing to 8-byte" - "\\[amd64\\].*sysret: RET without writing to 8-byte"
- "\\[amd64\\].*iret: RET without writing to 8-byte" - "\\[amd64\\].*iret: RET without writing to 8-byte"
- "\\[amd64\\].*exception: use of .* points beyond argument frame" - "\\[amd64\\].*exception: use of .* points beyond argument frame"
- "\\[amd64\\].*retjmp: use of .* points beyond argument frame"
- "\\[arm64\\].*HaltEl1ExceptionAndResume: unknown variable" - "\\[arm64\\].*HaltEl1ExceptionAndResume: unknown variable"
- "\\[arm64\\].*HaltEl1ExceptionAndResume: use of .* points beyond argument frame" - "\\[arm64\\].*HaltEl1ExceptionAndResume: use of .* points beyond argument frame"
assign: assign:
@@ -193,6 +194,7 @@ analyzers:
- pkg/sentry/pgalloc/pgalloc_unsafe.go # Special case. - pkg/sentry/pgalloc/pgalloc_unsafe.go # Special case.
- pkg/sentry/platform/kvm/bluepill_unsafe.go # Special case. - pkg/sentry/platform/kvm/bluepill_unsafe.go # Special case.
- pkg/sentry/platform/kvm/machine_unsafe.go # Special case. - pkg/sentry/platform/kvm/machine_unsafe.go # Special case.
- pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go # Special case.
- pkg/sentry/platform/pgalloc/pgalloc_unsafe.go # Special case. - pkg/sentry/platform/pgalloc/pgalloc_unsafe.go # Special case.
- pkg/sentry/platform/systrap/stub_unsafe.go # Special case. - pkg/sentry/platform/systrap/stub_unsafe.go # Special case.
- pkg/sentry/platform/systrap/syscall_thread_unsafe.go # Special case. - pkg/sentry/platform/systrap/syscall_thread_unsafe.go # Special case.
+1
View File
@@ -79,6 +79,7 @@ go_library(
"//pkg/sentry/platform", "//pkg/sentry/platform",
"//pkg/sentry/platform/interrupt", "//pkg/sentry/platform/interrupt",
"//pkg/sentry/time", "//pkg/sentry/time",
"//pkg/sigframe",
"//pkg/sighandling", "//pkg/sighandling",
"//pkg/sync", "//pkg/sync",
"@org_golang_x_sys//unix:go_default_library", "@org_golang_x_sys//unix:go_default_library",
-11
View File
@@ -15,17 +15,11 @@
package kvm package kvm
import ( import (
"fmt"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/ring0" "gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sighandling"
) )
// bluepill enters guest mode.
func bluepill(*vCPU)
// sighandler is the signal entry point. // sighandler is the signal entry point.
func sighandler() func sighandler()
@@ -100,11 +94,6 @@ func (c *vCPU) die(context *arch.SignalContext64, msg string) {
} }
func init() { func init() {
// Install the handler.
if err := sighandling.ReplaceSignalHandler(bluepillSignal, addrOfSighandler(), &savedHandler); err != nil {
panic(fmt.Sprintf("Unable to set handler for signal %d: %v", bluepillSignal, err))
}
// Extract the address for the trampoline. // Extract the address for the trampoline.
dieTrampolineAddr = addrOfDieTrampoline() dieTrampolineAddr = addrOfDieTrampoline()
} }
@@ -28,6 +28,10 @@ var (
bluepillSignal = unix.SIGSEGV bluepillSignal = unix.SIGSEGV
) )
func bluepillArchVCPU(context *arch.SignalContext64) *vCPU {
return vCPUPtr(uintptr(context.Rax))
}
// bluepillArchEnter is called during bluepillEnter. // bluepillArchEnter is called during bluepillEnter.
// //
//go:nosplit //go:nosplit
+16 -63
View File
@@ -40,69 +40,6 @@
// System call definitions. // System call definitions.
#define SYS_MMAP 9 #define SYS_MMAP 9
// See bluepill.go.
TEXT ·bluepill(SB),NOSPLIT|NOFRAME,$0
begin:
MOVQ arg+0(FP), AX
LEAQ VCPU_CPU(AX), BX
// The gorountine stack will be changed in guest which renders
// the frame pointer outdated and misleads perf tools.
// Disconnect the frame-chain with the zeroed frame pointer
// when it is saved in the frame in bluepillHandler().
MOVQ BP, CX
MOVQ $0, BP
BYTE CLI;
MOVQ CX, BP
check_vcpu:
MOVQ ENTRY_CPU_SELF(GS), CX
CMPQ BX, CX
JE right_vCPU
wrong_vcpu:
CALL ·redpill(SB)
JMP begin
right_vCPU:
RET
// sighandler: see bluepill.go for documentation.
//
// The arguments are the following:
//
// DI - The signal number.
// SI - Pointer to siginfo_t structure.
// DX - Pointer to ucontext structure.
//
TEXT ·sighandler(SB),NOSPLIT|NOFRAME,$0
// Check if the signal is from the kernel.
MOVQ $0x80, CX
CMPL CX, 0x8(SI)
JNE fallback
// Check if RIP is disable interrupts.
MOVQ CONTEXT_RIP(DX), CX
CMPQ CX, $0x0
JE fallback
CMPB 0(CX), CLI
JNE fallback
// Call the bluepillHandler.
PUSHQ DX // First argument (context).
CALL ·bluepillHandler(SB) // Call the handler.
POPQ DX // Discard the argument.
RET
fallback:
// Jump to the previous signal handler.
XORQ CX, CX
MOVQ ·savedHandler(SB), AX
JMP AX
// func addrOfSighandler() uintptr
TEXT ·addrOfSighandler(SB), $0-8
MOVQ $·sighandler(SB), AX
MOVQ AX, ret+0(FP)
RET
TEXT ·sigsysHandler(SB),NOSPLIT|NOFRAME,$0 TEXT ·sigsysHandler(SB),NOSPLIT|NOFRAME,$0
// Check if the signal is from the kernel. // Check if the signal is from the kernel.
MOVQ $1, CX MOVQ $1, CX
@@ -122,6 +59,17 @@ fallback:
MOVQ ·savedSigsysHandler(SB), AX MOVQ ·savedSigsysHandler(SB), AX
JMP AX JMP AX
TEXT ·rflags(SB), $8-8
PUSHFQ
POPQ AX
MOVQ AX, ret+0(FP)
RET
TEXT ·addrOfBluepillUserHandler(SB), $0-8
MOVQ $·bluepillUserHandler(SB), AX
MOVQ AX, ret+0(FP)
RET
// func addrOfSighandler() uintptr // func addrOfSighandler() uintptr
TEXT ·addrOfSigsysHandler(SB), $0-8 TEXT ·addrOfSigsysHandler(SB), $0-8
MOVQ $·sigsysHandler(SB), AX MOVQ $·sigsysHandler(SB), AX
@@ -139,3 +87,8 @@ TEXT ·addrOfDieTrampoline(SB), $0-8
MOVQ $·dieTrampoline(SB), AX MOVQ $·dieTrampoline(SB), AX
MOVQ AX, ret+0(FP) MOVQ AX, ret+0(FP)
RET RET
TEXT ·currentCPU(SB), $0-8
MOVQ ENTRY_CPU_SELF(GS), AX
MOVQ AX, ret+0(FP)
RET
@@ -18,11 +18,16 @@
package kvm package kvm
import ( import (
"fmt"
"unsafe" "unsafe"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/ring0" "gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sigframe"
) )
// dieArchSetup initializes the state for dieTrampoline. // dieArchSetup initializes the state for dieTrampoline.
@@ -141,3 +146,70 @@ func bluepillReadyStopGuest(c *vCPU) bool {
func bluepillArchHandleExit(c *vCPU, context unsafe.Pointer) { func bluepillArchHandleExit(c *vCPU, context unsafe.Pointer) {
c.die(bluepillArchContext(context), "unknown") c.die(bluepillArchContext(context), "unknown")
} }
func addrOfBluepillUserHandler() uintptr
func rflags() uint64
const _RFLAGS_IF = 1 << 9
func currentCPU() *vCPU
// bluepill enters guest mode.
//
//go:nosplit
func bluepill(c *vCPU) {
// Interrupts are always disabled in the VM.
if rflags()&_RFLAGS_IF == 0 {
if currentCPU() == c {
// Already in the vm.
return
}
// Wrong vCPU, switch to the right one.
redpill()
}
// Block all signals.
sigmask := linux.SignalSet(^uint64(0))
c.bluepillSigframe.MContext.Rax = uint64(uintptr(unsafe.Pointer(c)))
if err := sigframe.CallWithSignalFrame(
c.bluepillStack, addrOfBluepillUserHandler(),
c.bluepillSigframe, c.bluepillSigframeFPState, &sigmask); err != nil {
throw("failed to swallow the bluepill")
}
}
// +checkescape:all
//
//go:nosplit
func bluepillUserHandler(frame uintptr) {
// Sanitize the registers; interrupts must always be disabled.
context := bluepillArchContext(unsafe.Pointer(frame))
c := vCPUPtr(uintptr(context.Rax))
bluepillHandler(unsafe.Pointer(frame))
sigframe.Sigreturn(c.bluepillSigframe)
}
//go:nosplit
func (c *vCPU) initBluepillHandler() error {
stackSize := uintptr(hostarch.PageSize)
maxFPUSizeUint, fpuAlignmentUint := cpuid.HostFeatureSet().ExtendedStateSize()
fpuAlignment := uintptr(fpuAlignmentUint)
maxFPUSize := uintptr(maxFPUSizeUint)
fpuOffset := (unsafe.Sizeof(arch.UContext64{}) + fpuAlignment - 1) / fpuAlignment * fpuAlignment
mappingSize, _ := hostarch.PageRoundUp(stackSize + maxFPUSize + fpuOffset)
addr, _, errno := unix.Syscall6(unix.SYS_MMAP, 0, mappingSize,
uintptr(unix.PROT_READ|unix.PROT_WRITE),
uintptr(unix.MAP_PRIVATE|unix.MAP_ANONYMOUS),
0, 0)
if errno != 0 {
return fmt.Errorf("mmap failed: %d", errno)
}
c.bluepillStack = addr + stackSize
c.bluepillSigframe = (*arch.UContext64)(unsafe.Pointer(addr + stackSize))
c.bluepillSigframeFPState = addr + stackSize + fpuOffset
return nil
}
+15
View File
@@ -18,9 +18,12 @@
package kvm package kvm
import ( import (
"fmt"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/ring0" "gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sighandling"
) )
var ( var (
@@ -28,6 +31,11 @@ var (
bluepillSignal = unix.SIGILL bluepillSignal = unix.SIGILL
) )
// bluepill enters guest mode.
//
//go:nosplit
func bluepill(c *vCPU)
// getTLS returns the value of TPIDR_EL0 register. // getTLS returns the value of TPIDR_EL0 register.
// //
//go:nosplit //go:nosplit
@@ -130,3 +138,10 @@ func (c *vCPU) KernelException(vector ring0.Vector) {
//go:nosplit //go:nosplit
func (c *vCPU) hltSanityCheck() { func (c *vCPU) hltSanityCheck() {
} }
func init() {
// Install the handler.
if err := sighandling.ReplaceSignalHandler(bluepillSignal, addrOfSighandler(), &savedHandler); err != nil {
panic(fmt.Sprintf("Unable to set handler for signal %d: %v", bluepillSignal, err))
}
}
+1 -1
View File
@@ -50,7 +50,7 @@ TEXT ·setTLS(SB),NOSPLIT,$0-8
// See bluepill.go. // See bluepill.go.
TEXT ·bluepill(SB),NOSPLIT,$0 TEXT ·bluepill(SB),NOSPLIT,$0
begin: begin:
MOVD arg+0(FP), R8 MOVD c+0(FP), R8
MOVD $VCPU_CPU(R8), R9 MOVD $VCPU_CPU(R8), R9
ORR $0xffff000000000000, R9, R9 ORR $0xffff000000000000, R9, R9
// Trigger sigill. // Trigger sigill.
+1
View File
@@ -510,6 +510,7 @@ func BenchmarkKernelSyscall(b *testing.B) {
applicationTest(b, true, testutil.AddrOfGetpid(), func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool { applicationTest(b, true, testutil.AddrOfGetpid(), func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
// iteration does not include machine.Get() / machine.Put(). // iteration does not include machine.Get() / machine.Put().
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
bluepill(c)
testutil.Getpid() testutil.Getpid()
} }
return false return false
+9
View File
@@ -30,6 +30,7 @@ import (
"gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/ring0" "gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/ring0/pagetables" "gvisor.dev/gvisor/pkg/ring0/pagetables"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/platform" "gvisor.dev/gvisor/pkg/sentry/platform"
ktime "gvisor.dev/gvisor/pkg/sentry/time" ktime "gvisor.dev/gvisor/pkg/sentry/time"
) )
@@ -77,6 +78,10 @@ type vCPUArchState struct {
// //
// This starts above fixedKernelPCID. // This starts above fixedKernelPCID.
PCIDs *pagetables.PCIDs PCIDs *pagetables.PCIDs
bluepillStack uintptr
bluepillSigframe *arch.UContext64
bluepillSigframeFPState uintptr
} }
const ( const (
@@ -95,6 +100,10 @@ const (
// initArchState initializes architecture-specific state. // initArchState initializes architecture-specific state.
func (c *vCPU) initArchState() error { func (c *vCPU) initArchState() error {
if err := c.initBluepillHandler(); err != nil {
return err
}
var ( var (
kernelSystemRegs systemRegs kernelSystemRegs systemRegs
kernelUserRegs userRegs kernelUserRegs userRegs
+36
View File
@@ -0,0 +1,36 @@
load("//tools:defs.bzl", "go_library", "go_test")
package(default_applicable_licenses = ["//:license"])
licenses(["notice"])
go_library(
name = "sigframe",
srcs = [
"sigframe.go",
"sigframe_amd64.s",
"sigframe_amd64_unsafe.go",
],
visibility = ["//visibility:public"],
deps = [
"//pkg/abi/linux",
"//pkg/sentry/arch",
"@org_golang_x_sys//unix:go_default_library",
],
)
go_test(
name = "sigframe_test",
srcs = [
"sigframe_amd64_test.go",
"sigframe_amd64_test.s",
"sigframe_test.go",
],
library = ":sigframe",
deps = [
"//pkg/abi/linux",
"//pkg/hostarch",
"//pkg/sentry/arch",
"@org_golang_x_sys//unix:go_default_library",
],
)
+21
View File
@@ -0,0 +1,21 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package sigframe implements a mechanism to create a signal frame on the
// stack and execute a user-defined callback function within that context. The
// callback function can use the `sigreturn` system call to resuming the
// current thread with the state from the signal frame. This functionality can
// be helpful in scenarios where you need to simulate a signal handler-like
// behavior without triggering a signal.
package sigframe
+90
View File
@@ -0,0 +1,90 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "funcdata.h"
#include "textflag.h"
#define SIGMCTX 40 // +checkoffset arch UContext64.MContext
#define SIGCTX_R8 SIGMCTX+0 // +checkoffset arch SignalContext64.R8
#define SIGCTX_R9 SIGMCTX+8 // +checkoffset arch SignalContext64.R9
#define SIGCTX_R10 SIGMCTX+16 // +checkoffset arch SignalContext64.R10
#define SIGCTX_R11 SIGMCTX+24 // +checkoffset arch SignalContext64.R11
#define SIGCTX_R12 SIGMCTX+32 // +checkoffset arch SignalContext64.R12
#define SIGCTX_R13 SIGMCTX+40 // +checkoffset arch SignalContext64.R13
#define SIGCTX_R14 SIGMCTX+48 // +checkoffset arch SignalContext64.R14
#define SIGCTX_R15 SIGMCTX+56 // +checkoffset arch SignalContext64.R15
#define SIGCTX_RDI SIGMCTX+64 // +checkoffset arch SignalContext64.Rdi
#define SIGCTX_RSI SIGMCTX+72 // +checkoffset arch SignalContext64.Rsi
#define SIGCTX_RBP SIGMCTX+80 // +checkoffset arch SignalContext64.Rbp
#define SIGCTX_RBX SIGMCTX+88 // +checkoffset arch SignalContext64.Rbx
#define SIGCTX_RDX SIGMCTX+96 // +checkoffset arch SignalContext64.Rdx
#define SIGCTX_RAX SIGMCTX+104 // +checkoffset arch SignalContext64.Rax
#define SIGCTX_RCX SIGMCTX+112 // +checkoffset arch SignalContext64.Rcx
#define SIGCTX_RSP SIGMCTX+120 // +checkoffset arch SignalContext64.Rsp
#define SIGCTX_RIP SIGMCTX+128 // +checkoffset arch SignalContext64.Rip
#define SIGCTX_FL SIGMCTX+136 // +checkoffset arch SignalContext64.Eflags
#define SIGCTX_CS SIGMCTX+144 // +checkoffset arch SignalContext64.Cs
#define SIGCTX_GS SIGMCTX+146 // +checkoffset arch SignalContext64.Gs
#define SIGCTX_FS SIGMCTX+148 // +checkoffset arch SignalContext64.Fs
#define SIGCTX_SS SIGMCTX+150 // +checkoffset arch SignalContext64.Ss
#define SIGCTX_ERR SIGMCTX+152 // +checkoffset arch SignalContext64.Err
#define SIGCTX_TRAPNO SIGMCTX+160 // +checkoffset arch SignalContext64.Trapno
#define SIGCTX_MASK SIGMCTX+168 // +checkoffset arch SignalContext64.Oldmask
#define SIGCTX_CR2 SIGMCTX+176 // +checkoffset arch SignalContext64.Cr2
#define SIGCTX_FPSTATE SIGMCTX+184 // +checkoffset arch SignalContext64.Fpstate
// Callee-Save: RBX, RBP, and R12-R15 are preserved across function calls.
// Caller-Save: RAX, RCX, RDX, RSI, RDI, and R8-R11 are free to be used by
// the called function and may be overwritten.
// retjmp has to be updated when the stack frame size is changed.
TEXT ·callWithSignalFrame(SB),NOSPLIT,$8-32
MOVQ stack+0(FP), DI
MOVQ handler+8(FP), AX
MOVQ sigframe+16(FP), R8
MOVQ fpstate+24(FP), R9
MOVQ R9, SIGCTX_FPSTATE(R8)
MOVQ BX, SIGCTX_RBX(R8)
MOVQ BP, SIGCTX_RBP(R8)
MOVQ R12, SIGCTX_R12(R8)
MOVQ R13, SIGCTX_R13(R8)
MOVQ R14, SIGCTX_R14(R8)
MOVQ R15, SIGCTX_R15(R8)
PUSHFQ
POPQ R9
MOVQ R9, SIGCTX_FL(R8)
MOVQ SP, SIGCTX_RSP(R8)
MOVQ $·retjmp(SB),R9
MOVQ R9, SIGCTX_RIP(R8)
MOVQ $0x33, SIGCTX_CS(R8)
MOVQ $0x2b, SIGCTX_SS(R8)
MOVQ DI, SP
PUSHQ $0x0
MOVQ SP, BP
PUSHQ R8 // Save CPU.
CALL AX
#define __NR_rt_sigreturn 15 // +checkconst unix SYS_RT_SIGRETURN
TEXT ·Sigreturn(SB),NOSPLIT,$0-8
MOVQ sigframeAddr+0(FP), SP
MOVQ $__NR_rt_sigreturn, AX
SYSCALL
// retjmp is the return sequence from callWithSignalFrame. It is used to set
// RIP on a signal frame.
TEXT ·retjmp(SB),NOSPLIT,$0-0
MOVQ 8(SP), BP
ADDQ $0x10,SP
RET
+54
View File
@@ -0,0 +1,54 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build amd64
// +build amd64
package sigframe
import (
"testing"
"unsafe"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
)
func TestUserSignalHandler(t *testing.T) {
addr, _, _ := unix.Syscall6(unix.SYS_MMAP, 0, hostarch.PageSize*4, uintptr(unix.PROT_READ|unix.PROT_WRITE),
uintptr(unix.MAP_PRIVATE|unix.MAP_ANONYMOUS),
0, 0)
set := linux.MakeSignalSet(linux.SIGURG)
sigframe := (*arch.UContext64)(unsafe.Pointer(addr + hostarch.PageSize))
CallWithSignalFrame(addr+hostarch.PageSize, addrOfUserHandler(), sigframe, 0, &set)
}
func BenchmarkUserSigHandler(b *testing.B) {
addr, _, _ := unix.Syscall6(unix.SYS_MMAP, 0, hostarch.PageSize*4, uintptr(unix.PROT_READ|unix.PROT_WRITE),
uintptr(unix.MAP_PRIVATE|unix.MAP_ANONYMOUS),
0, 0)
set := linux.MakeSignalSet(linux.SIGURG)
sigframe := (*arch.UContext64)(unsafe.Pointer(addr + hostarch.PageSize))
for i := 0; i < b.N; i++ {
CallWithSignalFrame(addr+hostarch.PageSize, addrOfUserHandler(), sigframe, 0, &set)
}
}
func addrOfUserHandler() uintptr
func userHandler(sigframe *arch.UContext64) {
Sigreturn(sigframe)
}
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "funcdata.h"
#include "textflag.h"
// ADDR_OF_FUNC defines a function named 'name' that returns the address of
// 'symbol'.
#define ADDR_OF_FUNC(name, symbol) \
TEXT name,$0-8; \
MOVQ $symbol, AX; \
MOVQ AX, ret+0(FP); \
RET
ADDR_OF_FUNC(·addrOfUserHandler(SB), ·userHandler(SB));
+57
View File
@@ -0,0 +1,57 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build amd64
// +build amd64
package sigframe
import (
"unsafe"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/sentry/arch"
)
func callWithSignalFrame(stack uintptr, handler uintptr, sigframe *arch.UContext64, fpstate uintptr)
//go:linkname throw runtime.throw
func throw(s string)
// CallWithSignalFrame sets up a signal frame on the stack and executes a
// user-defined callback function within that context.
//
// Caller-save registers can be used for passing arguments to the handler.
// These registers must be pre-set within the signal frame.
//
//go:nosplit
func CallWithSignalFrame(stack uintptr, handlerAddr uintptr, sigframe *arch.UContext64, fpstate uintptr, sigmask *linux.SignalSet) error {
_, _, errno := unix.RawSyscall6(
unix.SYS_RT_SIGPROCMASK, linux.SIG_BLOCK,
uintptr(unsafe.Pointer(sigmask)),
uintptr(unsafe.Pointer(&sigframe.Sigset)),
linux.SignalSetSize,
0, 0)
if errno != 0 {
return errno
}
callWithSignalFrame(stack, handlerAddr, sigframe, fpstate)
return nil
}
// Sigreturn restores the thread state from the signal frame.
func Sigreturn(sigframeAddr *arch.UContext64)
func retjmp()
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sigframe
import (
"testing"
)
func TestDummy(t *testing.T) {
}