mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -106,6 +106,7 @@ analyzers:
|
||||
- "\\[amd64\\].*sysret: RET without writing to 8-byte"
|
||||
- "\\[amd64\\].*iret: RET without writing to 8-byte"
|
||||
- "\\[amd64\\].*exception: use of .* points beyond argument frame"
|
||||
- "\\[amd64\\].*retjmp: use of .* points beyond argument frame"
|
||||
- "\\[arm64\\].*HaltEl1ExceptionAndResume: unknown variable"
|
||||
- "\\[arm64\\].*HaltEl1ExceptionAndResume: use of .* points beyond argument frame"
|
||||
assign:
|
||||
@@ -193,6 +194,7 @@ analyzers:
|
||||
- pkg/sentry/pgalloc/pgalloc_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/bluepill_amd64_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/syscall_thread_unsafe.go # Special case.
|
||||
|
||||
@@ -79,6 +79,7 @@ go_library(
|
||||
"//pkg/sentry/platform",
|
||||
"//pkg/sentry/platform/interrupt",
|
||||
"//pkg/sentry/time",
|
||||
"//pkg/sigframe",
|
||||
"//pkg/sighandling",
|
||||
"//pkg/sync",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
|
||||
@@ -15,17 +15,11 @@
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/ring0"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sighandling"
|
||||
)
|
||||
|
||||
// bluepill enters guest mode.
|
||||
func bluepill(*vCPU)
|
||||
|
||||
// sighandler is the signal entry point.
|
||||
func sighandler()
|
||||
|
||||
@@ -100,11 +94,6 @@ func (c *vCPU) die(context *arch.SignalContext64, msg string) {
|
||||
}
|
||||
|
||||
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.
|
||||
dieTrampolineAddr = addrOfDieTrampoline()
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ var (
|
||||
bluepillSignal = unix.SIGSEGV
|
||||
)
|
||||
|
||||
func bluepillArchVCPU(context *arch.SignalContext64) *vCPU {
|
||||
return vCPUPtr(uintptr(context.Rax))
|
||||
}
|
||||
|
||||
// bluepillArchEnter is called during bluepillEnter.
|
||||
//
|
||||
//go:nosplit
|
||||
|
||||
@@ -40,69 +40,6 @@
|
||||
// System call definitions.
|
||||
#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
|
||||
// Check if the signal is from the kernel.
|
||||
MOVQ $1, CX
|
||||
@@ -122,6 +59,17 @@ fallback:
|
||||
MOVQ ·savedSigsysHandler(SB), 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
|
||||
TEXT ·addrOfSigsysHandler(SB), $0-8
|
||||
MOVQ $·sigsysHandler(SB), AX
|
||||
@@ -139,3 +87,8 @@ TEXT ·addrOfDieTrampoline(SB), $0-8
|
||||
MOVQ $·dieTrampoline(SB), AX
|
||||
MOVQ AX, ret+0(FP)
|
||||
RET
|
||||
|
||||
TEXT ·currentCPU(SB), $0-8
|
||||
MOVQ ENTRY_CPU_SELF(GS), AX
|
||||
MOVQ AX, ret+0(FP)
|
||||
RET
|
||||
|
||||
@@ -18,11 +18,16 @@
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"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/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sigframe"
|
||||
)
|
||||
|
||||
// dieArchSetup initializes the state for dieTrampoline.
|
||||
@@ -141,3 +146,70 @@ func bluepillReadyStopGuest(c *vCPU) bool {
|
||||
func bluepillArchHandleExit(c *vCPU, context unsafe.Pointer) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -18,9 +18,12 @@
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/ring0"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sighandling"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -28,6 +31,11 @@ var (
|
||||
bluepillSignal = unix.SIGILL
|
||||
)
|
||||
|
||||
// bluepill enters guest mode.
|
||||
//
|
||||
//go:nosplit
|
||||
func bluepill(c *vCPU)
|
||||
|
||||
// getTLS returns the value of TPIDR_EL0 register.
|
||||
//
|
||||
//go:nosplit
|
||||
@@ -130,3 +138,10 @@ func (c *vCPU) KernelException(vector ring0.Vector) {
|
||||
//go:nosplit
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ TEXT ·setTLS(SB),NOSPLIT,$0-8
|
||||
// See bluepill.go.
|
||||
TEXT ·bluepill(SB),NOSPLIT,$0
|
||||
begin:
|
||||
MOVD arg+0(FP), R8
|
||||
MOVD c+0(FP), R8
|
||||
MOVD $VCPU_CPU(R8), R9
|
||||
ORR $0xffff000000000000, R9, R9
|
||||
// Trigger sigill.
|
||||
|
||||
@@ -510,6 +510,7 @@ func BenchmarkKernelSyscall(b *testing.B) {
|
||||
applicationTest(b, true, testutil.AddrOfGetpid(), func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
|
||||
// iteration does not include machine.Get() / machine.Put().
|
||||
for i := 0; i < b.N; i++ {
|
||||
bluepill(c)
|
||||
testutil.Getpid()
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/ring0"
|
||||
"gvisor.dev/gvisor/pkg/ring0/pagetables"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform"
|
||||
ktime "gvisor.dev/gvisor/pkg/sentry/time"
|
||||
)
|
||||
@@ -77,6 +78,10 @@ type vCPUArchState struct {
|
||||
//
|
||||
// This starts above fixedKernelPCID.
|
||||
PCIDs *pagetables.PCIDs
|
||||
|
||||
bluepillStack uintptr
|
||||
bluepillSigframe *arch.UContext64
|
||||
bluepillSigframeFPState uintptr
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -95,6 +100,10 @@ const (
|
||||
|
||||
// initArchState initializes architecture-specific state.
|
||||
func (c *vCPU) initArchState() error {
|
||||
if err := c.initBluepillHandler(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
kernelSystemRegs systemRegs
|
||||
kernelUserRegs userRegs
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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));
|
||||
@@ -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()
|
||||
@@ -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) {
|
||||
}
|
||||
Reference in New Issue
Block a user