mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add initial ptrace stub and syscall support for arm64.
Signed-off-by: Haibo Xu <haibo.xu@arm.com> Change-Id: I1dbd23bb240cca71d0cc30fc75ca5be28cb4c37c PiperOrigin-RevId: 262619519
This commit is contained in:
@@ -89,3 +89,17 @@ const (
|
||||
// AT_SYSINFO_EHDR is the address of the VDSO.
|
||||
AT_SYSINFO_EHDR = 33
|
||||
)
|
||||
|
||||
// ELF ET_CORE and ptrace GETREGSET/SETREGSET register set types.
|
||||
//
|
||||
// See include/uapi/linux/elf.h.
|
||||
const (
|
||||
// NT_PRSTATUS is for general purpose register.
|
||||
NT_PRSTATUS = 0x1
|
||||
|
||||
// NT_PRFPREG is for float point register.
|
||||
NT_PRFPREG = 0x2
|
||||
|
||||
// NT_X86_XSTATE is for x86 extended state using xsave.
|
||||
NT_X86_XSTATE = 0x202
|
||||
)
|
||||
|
||||
@@ -7,13 +7,17 @@ go_library(
|
||||
srcs = [
|
||||
"filters.go",
|
||||
"ptrace.go",
|
||||
"ptrace_amd64.go",
|
||||
"ptrace_arm64.go",
|
||||
"ptrace_unsafe.go",
|
||||
"stub_amd64.s",
|
||||
"stub_arm64.s",
|
||||
"stub_unsafe.go",
|
||||
"subprocess.go",
|
||||
"subprocess_amd64.go",
|
||||
"subprocess_arm64.go",
|
||||
"subprocess_linux.go",
|
||||
"subprocess_linux_amd64_unsafe.go",
|
||||
"subprocess_linux_unsafe.go",
|
||||
"subprocess_unsafe.go",
|
||||
],
|
||||
importpath = "gvisor.dev/gvisor/pkg/sentry/platform/ptrace",
|
||||
|
||||
@@ -60,7 +60,7 @@ var (
|
||||
// maximum user address. This is valid only after a call to stubInit.
|
||||
//
|
||||
// We attempt to link the stub here, and adjust downward as needed.
|
||||
stubStart uintptr = 0x7fffffff0000
|
||||
stubStart uintptr = stubInitAddress
|
||||
|
||||
// stubEnd is the first byte past the end of the stub, as with
|
||||
// stubStart this is valid only after a call to stubInit.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2019 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 ptrace
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
)
|
||||
|
||||
// fpRegSet returns the GETREGSET/SETREGSET register set type to be used.
|
||||
func fpRegSet(useXsave bool) uintptr {
|
||||
if useXsave {
|
||||
return linux.NT_X86_XSTATE
|
||||
}
|
||||
return linux.NT_PRFPREG
|
||||
}
|
||||
|
||||
func stackPointer(r *syscall.PtraceRegs) uintptr {
|
||||
return uintptr(r.Rsp)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2019 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 ptrace
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
)
|
||||
|
||||
// fpRegSet returns the GETREGSET/SETREGSET register set type to be used.
|
||||
func fpRegSet(_ bool) uintptr {
|
||||
return linux.NT_PRFPREG
|
||||
}
|
||||
|
||||
func stackPointer(r *syscall.PtraceRegs) uintptr {
|
||||
return uintptr(r.Sp)
|
||||
}
|
||||
@@ -18,37 +18,23 @@ import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
)
|
||||
|
||||
// GETREGSET/SETREGSET register set types.
|
||||
//
|
||||
// See include/uapi/linux/elf.h.
|
||||
const (
|
||||
// _NT_PRFPREG is for x86 floating-point state without using xsave.
|
||||
_NT_PRFPREG = 0x2
|
||||
|
||||
// _NT_X86_XSTATE is for x86 extended state using xsave.
|
||||
_NT_X86_XSTATE = 0x202
|
||||
)
|
||||
|
||||
// fpRegSet returns the GETREGSET/SETREGSET register set type to be used.
|
||||
func fpRegSet(useXsave bool) uintptr {
|
||||
if useXsave {
|
||||
return _NT_X86_XSTATE
|
||||
}
|
||||
return _NT_PRFPREG
|
||||
}
|
||||
|
||||
// getRegs sets the regular register set.
|
||||
// getRegs gets the general purpose register set.
|
||||
func (t *thread) getRegs(regs *syscall.PtraceRegs) error {
|
||||
iovec := syscall.Iovec{
|
||||
Base: (*byte)(unsafe.Pointer(regs)),
|
||||
Len: uint64(unsafe.Sizeof(*regs)),
|
||||
}
|
||||
_, _, errno := syscall.RawSyscall6(
|
||||
syscall.SYS_PTRACE,
|
||||
syscall.PTRACE_GETREGS,
|
||||
syscall.PTRACE_GETREGSET,
|
||||
uintptr(t.tid),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(regs)),
|
||||
linux.NT_PRSTATUS,
|
||||
uintptr(unsafe.Pointer(&iovec)),
|
||||
0, 0)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
@@ -56,14 +42,18 @@ func (t *thread) getRegs(regs *syscall.PtraceRegs) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setRegs sets the regular register set.
|
||||
// setRegs sets the general purpose register set.
|
||||
func (t *thread) setRegs(regs *syscall.PtraceRegs) error {
|
||||
iovec := syscall.Iovec{
|
||||
Base: (*byte)(unsafe.Pointer(regs)),
|
||||
Len: uint64(unsafe.Sizeof(*regs)),
|
||||
}
|
||||
_, _, errno := syscall.RawSyscall6(
|
||||
syscall.SYS_PTRACE,
|
||||
syscall.PTRACE_SETREGS,
|
||||
syscall.PTRACE_SETREGSET,
|
||||
uintptr(t.tid),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(regs)),
|
||||
linux.NT_PRSTATUS,
|
||||
uintptr(unsafe.Pointer(&iovec)),
|
||||
0, 0)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
@@ -131,7 +121,7 @@ func (t *thread) getSignalInfo(si *arch.SignalInfo) error {
|
||||
//
|
||||
// Precondition: the OS thread must be locked and own t.
|
||||
func (t *thread) clone() (*thread, error) {
|
||||
r, ok := usermem.Addr(t.initRegs.Rsp).RoundUp()
|
||||
r, ok := usermem.Addr(stackPointer(&t.initRegs)).RoundUp()
|
||||
if !ok {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright 2019 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 SYS_GETPID 172
|
||||
#define SYS_EXIT 93
|
||||
#define SYS_KILL 129
|
||||
#define SYS_GETPPID 173
|
||||
#define SYS_PRCTL 167
|
||||
|
||||
#define SIGKILL 9
|
||||
#define SIGSTOP 19
|
||||
|
||||
#define PR_SET_PDEATHSIG 1
|
||||
|
||||
// stub bootstraps the child and sends itself SIGSTOP to wait for attach.
|
||||
//
|
||||
// R7 contains the expected PPID.
|
||||
//
|
||||
// This should not be used outside the context of a new ptrace child (as the
|
||||
// function is otherwise a bunch of nonsense).
|
||||
TEXT ·stub(SB),NOSPLIT,$0
|
||||
begin:
|
||||
// N.B. This loop only executes in the context of a single-threaded
|
||||
// fork child.
|
||||
|
||||
MOVD $SYS_PRCTL, R8
|
||||
MOVD $PR_SET_PDEATHSIG, R0
|
||||
MOVD $SIGKILL, R1
|
||||
SVC
|
||||
|
||||
CMN $4095, R0
|
||||
BCS error
|
||||
|
||||
// If the parent already died before we called PR_SET_DEATHSIG then
|
||||
// we'll have an unexpected PPID.
|
||||
MOVD $SYS_GETPPID, R8
|
||||
SVC
|
||||
|
||||
CMP R0, R7
|
||||
BNE parent_dead
|
||||
|
||||
MOVD $SYS_GETPID, R8
|
||||
SVC
|
||||
|
||||
CMP $0x0, R0
|
||||
BLT error
|
||||
|
||||
// SIGSTOP to wait for attach.
|
||||
//
|
||||
// The SYSCALL instruction will be used for future syscall injection by
|
||||
// thread.syscall.
|
||||
MOVD $SYS_KILL, R8
|
||||
MOVD $SIGSTOP, R1
|
||||
SVC
|
||||
// The tracer may "detach" and/or allow code execution here in three cases:
|
||||
//
|
||||
// 1. New (traced) stub threads are explicitly detached by the
|
||||
// goroutine in newSubprocess. However, they are detached while in
|
||||
// group-stop, so they do not execute code here.
|
||||
//
|
||||
// 2. If a tracer thread exits, it implicitly detaches from the stub,
|
||||
// potentially allowing code execution here. However, the Go runtime
|
||||
// never exits individual threads, so this case never occurs.
|
||||
//
|
||||
// 3. subprocess.createStub clones a new stub process that is untraced,
|
||||
// thus executing this code. We setup the PDEATHSIG before SIGSTOPing
|
||||
// ourselves for attach by the tracer.
|
||||
//
|
||||
// R7 has been updated with the expected PPID.
|
||||
B begin
|
||||
|
||||
error:
|
||||
// Exit with -errno.
|
||||
NEG R0, R0
|
||||
MOVD $SYS_EXIT, R8
|
||||
SVC
|
||||
HLT
|
||||
|
||||
parent_dead:
|
||||
MOVD $SYS_EXIT, R8
|
||||
MOVD $1, R0
|
||||
SVC
|
||||
HLT
|
||||
|
||||
// stubCall calls the stub function at the given address with the given PPID.
|
||||
//
|
||||
// This is a distinct function because stub, above, may be mapped at any
|
||||
// arbitrary location, and stub has a specific binary API (see above).
|
||||
TEXT ·stubCall(SB),NOSPLIT,$0-16
|
||||
MOVD addr+0(FP), R0
|
||||
MOVD pid+8(FP), R7
|
||||
B (R0)
|
||||
@@ -28,6 +28,16 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
)
|
||||
|
||||
// Linux kernel errnos which "should never be seen by user programs", but will
|
||||
// be revealed to ptrace syscall exit tracing.
|
||||
//
|
||||
// These constants are only used in subprocess.go.
|
||||
const (
|
||||
ERESTARTSYS = syscall.Errno(512)
|
||||
ERESTARTNOINTR = syscall.Errno(513)
|
||||
ERESTARTNOHAND = syscall.Errno(514)
|
||||
)
|
||||
|
||||
// globalPool exists to solve two distinct problems:
|
||||
//
|
||||
// 1) Subprocesses can't always be killed properly (see Release).
|
||||
@@ -282,7 +292,7 @@ func (t *thread) grabInitRegs() {
|
||||
if err := t.getRegs(&t.initRegs); err != nil {
|
||||
panic(fmt.Sprintf("ptrace get regs failed: %v", err))
|
||||
}
|
||||
t.initRegs.Rip -= initRegsRipAdjustment
|
||||
t.adjustInitRegsRip()
|
||||
}
|
||||
|
||||
// detach detaches from the thread.
|
||||
|
||||
@@ -28,20 +28,13 @@ const (
|
||||
// maximumUserAddress is the largest possible user address.
|
||||
maximumUserAddress = 0x7ffffffff000
|
||||
|
||||
// stubInitAddress is the initial attempt link address for the stub.
|
||||
stubInitAddress = 0x7fffffff0000
|
||||
|
||||
// initRegsRipAdjustment is the size of the syscall instruction.
|
||||
initRegsRipAdjustment = 2
|
||||
)
|
||||
|
||||
// Linux kernel errnos which "should never be seen by user programs", but will
|
||||
// be revealed to ptrace syscall exit tracing.
|
||||
//
|
||||
// These constants are used in subprocess.go.
|
||||
const (
|
||||
ERESTARTSYS = syscall.Errno(512)
|
||||
ERESTARTNOINTR = syscall.Errno(513)
|
||||
ERESTARTNOHAND = syscall.Errno(514)
|
||||
)
|
||||
|
||||
// resetSysemuRegs sets up emulation registers.
|
||||
//
|
||||
// This should be called prior to calling sysemu.
|
||||
@@ -139,3 +132,14 @@ func dumpRegs(regs *syscall.PtraceRegs) string {
|
||||
|
||||
return m.String()
|
||||
}
|
||||
|
||||
// adjustInitregsRip adjust the current register RIP value to
|
||||
// be just before the system call instruction excution
|
||||
func (t *thread) adjustInitRegsRip() {
|
||||
t.initRegs.Rip -= initRegsRipAdjustment
|
||||
}
|
||||
|
||||
// Pass the expected PPID to the child via R15 when creating stub process
|
||||
func initChildProcessPPID(initregs *syscall.PtraceRegs, ppid int32) {
|
||||
initregs.R15 = uint64(ppid)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
// +build arm64
|
||||
|
||||
package ptrace
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
)
|
||||
|
||||
const (
|
||||
// maximumUserAddress is the largest possible user address.
|
||||
maximumUserAddress = 0xfffffffff000
|
||||
|
||||
// stubInitAddress is the initial attempt link address for the stub.
|
||||
// Only support 48bits VA currently.
|
||||
stubInitAddress = 0xffffffff0000
|
||||
|
||||
// initRegsRipAdjustment is the size of the svc instruction.
|
||||
initRegsRipAdjustment = 4
|
||||
)
|
||||
|
||||
// resetSysemuRegs sets up emulation registers.
|
||||
//
|
||||
// This should be called prior to calling sysemu.
|
||||
func (s *subprocess) resetSysemuRegs(regs *syscall.PtraceRegs) {
|
||||
}
|
||||
|
||||
// createSyscallRegs sets up syscall registers.
|
||||
//
|
||||
// This should be called to generate registers for a system call.
|
||||
func createSyscallRegs(initRegs *syscall.PtraceRegs, sysno uintptr, args ...arch.SyscallArgument) syscall.PtraceRegs {
|
||||
// Copy initial registers (Pc, Sp, etc.).
|
||||
regs := *initRegs
|
||||
|
||||
// Set our syscall number.
|
||||
// r8 for the syscall number.
|
||||
// r0-r6 is used to store the parameters.
|
||||
regs.Regs[8] = uint64(sysno)
|
||||
if len(args) >= 1 {
|
||||
regs.Regs[0] = args[0].Uint64()
|
||||
}
|
||||
if len(args) >= 2 {
|
||||
regs.Regs[1] = args[1].Uint64()
|
||||
}
|
||||
if len(args) >= 3 {
|
||||
regs.Regs[2] = args[2].Uint64()
|
||||
}
|
||||
if len(args) >= 4 {
|
||||
regs.Regs[3] = args[3].Uint64()
|
||||
}
|
||||
if len(args) >= 5 {
|
||||
regs.Regs[4] = args[4].Uint64()
|
||||
}
|
||||
if len(args) >= 6 {
|
||||
regs.Regs[5] = args[5].Uint64()
|
||||
}
|
||||
|
||||
return regs
|
||||
}
|
||||
|
||||
// isSingleStepping determines if the registers indicate single-stepping.
|
||||
func isSingleStepping(regs *syscall.PtraceRegs) bool {
|
||||
// Refer to the ARM SDM D2.12.3: software step state machine
|
||||
// return (regs.Pstate.SS == 1) && (MDSCR_EL1.SS == 1).
|
||||
//
|
||||
// Since the host Linux kernel will set MDSCR_EL1.SS on our behalf
|
||||
// when we call a single-step ptrace command, we only need to check
|
||||
// the Pstate.SS bit here.
|
||||
return (regs.Pstate & arch.ARMTrapFlag) != 0
|
||||
}
|
||||
|
||||
// updateSyscallRegs updates registers after finishing sysemu.
|
||||
func updateSyscallRegs(regs *syscall.PtraceRegs) {
|
||||
// No special work is necessary.
|
||||
return
|
||||
}
|
||||
|
||||
// syscallReturnValue extracts a sensible return from registers.
|
||||
func syscallReturnValue(regs *syscall.PtraceRegs) (uintptr, error) {
|
||||
rval := int64(regs.Regs[0])
|
||||
if rval < 0 {
|
||||
return 0, syscall.Errno(-rval)
|
||||
}
|
||||
return uintptr(rval), nil
|
||||
}
|
||||
|
||||
func dumpRegs(regs *syscall.PtraceRegs) string {
|
||||
var m strings.Builder
|
||||
|
||||
fmt.Fprintf(&m, "Registers:\n")
|
||||
|
||||
for i := 0; i < 31; i++ {
|
||||
fmt.Fprintf(&m, "\tRegs[%d]\t = %016x\n", i, regs.Regs[i])
|
||||
}
|
||||
fmt.Fprintf(&m, "\tSp\t = %016x\n", regs.Sp)
|
||||
fmt.Fprintf(&m, "\tPc\t = %016x\n", regs.Pc)
|
||||
fmt.Fprintf(&m, "\tPstate\t = %016x\n", regs.Pstate)
|
||||
|
||||
return m.String()
|
||||
}
|
||||
|
||||
// adjustInitregsRip adjust the current register RIP value to
|
||||
// be just before the system call instruction excution
|
||||
func (t *thread) adjustInitRegsRip() {
|
||||
t.initRegs.Pc -= initRegsRipAdjustment
|
||||
}
|
||||
|
||||
// Pass the expected PPID to the child via X7 when creating stub process
|
||||
func initChildProcessPPID(initregs *syscall.PtraceRegs, ppid int32) {
|
||||
initregs.Regs[7] = uint64(ppid)
|
||||
}
|
||||
@@ -284,7 +284,7 @@ func (s *subprocess) createStub() (*thread, error) {
|
||||
|
||||
// Pass the expected PPID to the child via R15.
|
||||
regs := t.initRegs
|
||||
regs.R15 = uint64(t.tgid)
|
||||
initChildProcessPPID(®s, t.tgid)
|
||||
|
||||
// Call fork in a subprocess.
|
||||
//
|
||||
|
||||
+2
-1
@@ -12,7 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build amd64 linux
|
||||
// +build linux
|
||||
// +build amd64 arm64
|
||||
|
||||
package ptrace
|
||||
|
||||
Reference in New Issue
Block a user