diff --git a/nogo.yaml b/nogo.yaml index bd96f2194..b996883e7 100644 --- a/nogo.yaml +++ b/nogo.yaml @@ -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. diff --git a/pkg/sentry/platform/kvm/BUILD b/pkg/sentry/platform/kvm/BUILD index a25624d44..72acd018d 100644 --- a/pkg/sentry/platform/kvm/BUILD +++ b/pkg/sentry/platform/kvm/BUILD @@ -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", diff --git a/pkg/sentry/platform/kvm/bluepill.go b/pkg/sentry/platform/kvm/bluepill.go index 0e1aa89e5..6fe6f9749 100644 --- a/pkg/sentry/platform/kvm/bluepill.go +++ b/pkg/sentry/platform/kvm/bluepill.go @@ -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() } diff --git a/pkg/sentry/platform/kvm/bluepill_amd64.go b/pkg/sentry/platform/kvm/bluepill_amd64.go index 9e2fbc87f..66e986452 100644 --- a/pkg/sentry/platform/kvm/bluepill_amd64.go +++ b/pkg/sentry/platform/kvm/bluepill_amd64.go @@ -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 diff --git a/pkg/sentry/platform/kvm/bluepill_amd64.s b/pkg/sentry/platform/kvm/bluepill_amd64.s index 9d8cee40e..4f9128b58 100644 --- a/pkg/sentry/platform/kvm/bluepill_amd64.s +++ b/pkg/sentry/platform/kvm/bluepill_amd64.s @@ -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 diff --git a/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go b/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go index 60624ee54..a961b3bba 100644 --- a/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go +++ b/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go @@ -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 +} diff --git a/pkg/sentry/platform/kvm/bluepill_arm64.go b/pkg/sentry/platform/kvm/bluepill_arm64.go index f28e2a4e6..0dd2b7ee7 100644 --- a/pkg/sentry/platform/kvm/bluepill_arm64.go +++ b/pkg/sentry/platform/kvm/bluepill_arm64.go @@ -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)) + } +} diff --git a/pkg/sentry/platform/kvm/bluepill_arm64.s b/pkg/sentry/platform/kvm/bluepill_arm64.s index 901cb5526..52cb7f830 100644 --- a/pkg/sentry/platform/kvm/bluepill_arm64.s +++ b/pkg/sentry/platform/kvm/bluepill_arm64.s @@ -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. diff --git a/pkg/sentry/platform/kvm/kvm_test.go b/pkg/sentry/platform/kvm/kvm_test.go index f0ba03b08..29cf18df7 100644 --- a/pkg/sentry/platform/kvm/kvm_test.go +++ b/pkg/sentry/platform/kvm/kvm_test.go @@ -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 diff --git a/pkg/sentry/platform/kvm/machine_amd64.go b/pkg/sentry/platform/kvm/machine_amd64.go index 63149936c..0410521f0 100644 --- a/pkg/sentry/platform/kvm/machine_amd64.go +++ b/pkg/sentry/platform/kvm/machine_amd64.go @@ -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 diff --git a/pkg/sigframe/BUILD b/pkg/sigframe/BUILD new file mode 100644 index 000000000..fc9dc39c3 --- /dev/null +++ b/pkg/sigframe/BUILD @@ -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", + ], +) diff --git a/pkg/sigframe/sigframe.go b/pkg/sigframe/sigframe.go new file mode 100644 index 000000000..31c3a9a0d --- /dev/null +++ b/pkg/sigframe/sigframe.go @@ -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 diff --git a/pkg/sigframe/sigframe_amd64.s b/pkg/sigframe/sigframe_amd64.s new file mode 100644 index 000000000..2252232e4 --- /dev/null +++ b/pkg/sigframe/sigframe_amd64.s @@ -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 diff --git a/pkg/sigframe/sigframe_amd64_test.go b/pkg/sigframe/sigframe_amd64_test.go new file mode 100644 index 000000000..8e0bbcb8b --- /dev/null +++ b/pkg/sigframe/sigframe_amd64_test.go @@ -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) +} diff --git a/pkg/sigframe/sigframe_amd64_test.s b/pkg/sigframe/sigframe_amd64_test.s new file mode 100644 index 000000000..4217cb882 --- /dev/null +++ b/pkg/sigframe/sigframe_amd64_test.s @@ -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)); diff --git a/pkg/sigframe/sigframe_amd64_unsafe.go b/pkg/sigframe/sigframe_amd64_unsafe.go new file mode 100644 index 000000000..2cfe33643 --- /dev/null +++ b/pkg/sigframe/sigframe_amd64_unsafe.go @@ -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() diff --git a/pkg/sigframe/sigframe_test.go b/pkg/sigframe/sigframe_test.go new file mode 100644 index 000000000..8cc452925 --- /dev/null +++ b/pkg/sigframe/sigframe_test.go @@ -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) { +}