github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/platform/ptrace/subprocess_arm64.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build arm64 16 17 package ptrace 18 19 import ( 20 "fmt" 21 "strings" 22 23 "golang.org/x/sys/unix" 24 "github.com/SagerNet/gvisor/pkg/abi/linux" 25 "github.com/SagerNet/gvisor/pkg/seccomp" 26 "github.com/SagerNet/gvisor/pkg/sentry/arch" 27 ) 28 29 const ( 30 // maximumUserAddress is the largest possible user address. 31 maximumUserAddress = 0xfffffffff000 32 33 // stubInitAddress is the initial attempt link address for the stub. 34 // Only support 48bits VA currently. 35 stubInitAddress = 0xffffffff0000 36 37 // initRegsRipAdjustment is the size of the svc instruction. 38 initRegsRipAdjustment = 4 39 ) 40 41 // resetSysemuRegs sets up emulation registers. 42 // 43 // This should be called prior to calling sysemu. 44 func (t *thread) resetSysemuRegs(regs *arch.Registers) { 45 } 46 47 // createSyscallRegs sets up syscall registers. 48 // 49 // This should be called to generate registers for a system call. 50 func createSyscallRegs(initRegs *arch.Registers, sysno uintptr, args ...arch.SyscallArgument) arch.Registers { 51 // Copy initial registers (Pc, Sp, etc.). 52 regs := *initRegs 53 54 // Set our syscall number. 55 // r8 for the syscall number. 56 // r0-r6 is used to store the parameters. 57 regs.Regs[8] = uint64(sysno) 58 if len(args) >= 1 { 59 regs.Regs[0] = args[0].Uint64() 60 } 61 if len(args) >= 2 { 62 regs.Regs[1] = args[1].Uint64() 63 } 64 if len(args) >= 3 { 65 regs.Regs[2] = args[2].Uint64() 66 } 67 if len(args) >= 4 { 68 regs.Regs[3] = args[3].Uint64() 69 } 70 if len(args) >= 5 { 71 regs.Regs[4] = args[4].Uint64() 72 } 73 if len(args) >= 6 { 74 regs.Regs[5] = args[5].Uint64() 75 } 76 77 return regs 78 } 79 80 // isSingleStepping determines if the registers indicate single-stepping. 81 func isSingleStepping(regs *arch.Registers) bool { 82 // Refer to the ARM SDM D2.12.3: software step state machine 83 // return (regs.Pstate.SS == 1) && (MDSCR_EL1.SS == 1). 84 // 85 // Since the host Linux kernel will set MDSCR_EL1.SS on our behalf 86 // when we call a single-step ptrace command, we only need to check 87 // the Pstate.SS bit here. 88 return (regs.Pstate & arch.ARMTrapFlag) != 0 89 } 90 91 // updateSyscallRegs updates registers after finishing sysemu. 92 func updateSyscallRegs(regs *arch.Registers) { 93 // No special work is necessary. 94 return 95 } 96 97 // syscallReturnValue extracts a sensible return from registers. 98 func syscallReturnValue(regs *arch.Registers) (uintptr, error) { 99 rval := int64(regs.Regs[0]) 100 if rval < 0 { 101 return 0, unix.Errno(-rval) 102 } 103 return uintptr(rval), nil 104 } 105 106 func dumpRegs(regs *arch.Registers) string { 107 var m strings.Builder 108 109 fmt.Fprintf(&m, "Registers:\n") 110 111 for i := 0; i < 31; i++ { 112 fmt.Fprintf(&m, "\tRegs[%d]\t = %016x\n", i, regs.Regs[i]) 113 } 114 fmt.Fprintf(&m, "\tSp\t = %016x\n", regs.Sp) 115 fmt.Fprintf(&m, "\tPc\t = %016x\n", regs.Pc) 116 fmt.Fprintf(&m, "\tPstate\t = %016x\n", regs.Pstate) 117 118 return m.String() 119 } 120 121 // adjustInitregsRip adjust the current register RIP value to 122 // be just before the system call instruction excution 123 func (t *thread) adjustInitRegsRip() { 124 t.initRegs.Pc -= initRegsRipAdjustment 125 } 126 127 // Pass the expected PPID to the child via X7 when creating stub process 128 func initChildProcessPPID(initregs *arch.Registers, ppid int32) { 129 initregs.Regs[7] = uint64(ppid) 130 // R9 has to be set to 1 when creating stub process. 131 initregs.Regs[9] = 1 132 } 133 134 // patchSignalInfo patches the signal info to account for hitting the seccomp 135 // filters from vsyscall emulation, specified below. We allow for SIGSYS as a 136 // synchronous trap, but patch the structure to appear like a SIGSEGV with the 137 // Rip as the faulting address. 138 // 139 // Note that this should only be called after verifying that the signalInfo has 140 // been generated by the kernel. 141 func patchSignalInfo(regs *arch.Registers, signalInfo *linux.SignalInfo) { 142 if linux.Signal(signalInfo.Signo) == linux.SIGSYS { 143 signalInfo.Signo = int32(linux.SIGSEGV) 144 145 // Unwind the kernel emulation, if any has occurred. A SIGSYS is delivered 146 // with the si_call_addr field pointing to the current RIP. This field 147 // aligns with the si_addr field for a SIGSEGV, so we don't need to touch 148 // anything there. We do need to unwind emulation however, so we set the 149 // instruction pointer to the faulting value, and "unpop" the stack. 150 regs.Pc = signalInfo.Addr() 151 regs.Sp -= 8 152 } 153 } 154 155 // Noop on arm64. 156 // 157 //go:nosplit 158 func enableCpuidFault() { 159 } 160 161 // appendArchSeccompRules append architecture specific seccomp rules when creating BPF program. 162 // Ref attachedThread() for more detail. 163 func appendArchSeccompRules(rules []seccomp.RuleSet, defaultAction linux.BPFAction) []seccomp.RuleSet { 164 return rules 165 } 166 167 // probeSeccomp returns true if seccomp is run after ptrace notifications, 168 // which is generally the case for kernel version >= 4.8. 169 // 170 // On arm64, the support of PTRACE_SYSEMU was added in the 5.3 kernel, so 171 // probeSeccomp can always return true. 172 func probeSeccomp() bool { 173 return true 174 } 175 176 func (s *subprocess) arm64SyscallWorkaround(t *thread, regs *arch.Registers) { 177 // On ARM64, when ptrace stops on a system call, it uses the x7 178 // register to indicate whether the stop has been signalled from 179 // syscall entry or syscall exit. This means that we can't get a value 180 // of this register and we can't change it. More details are in the 181 // comment for tracehook_report_syscall in arch/arm64/kernel/ptrace.c. 182 // 183 // This happens only if we stop on a system call, so let's queue a 184 // signal, resume a stub thread and catch it on a signal handling. 185 t.NotifyInterrupt() 186 for { 187 if _, _, errno := unix.RawSyscall6( 188 unix.SYS_PTRACE, 189 unix.PTRACE_SYSEMU, 190 uintptr(t.tid), 0, 0, 0, 0); errno != 0 { 191 panic(fmt.Sprintf("ptrace sysemu failed: %v", errno)) 192 } 193 194 // Wait for the syscall-enter stop. 195 sig := t.wait(stopped) 196 if sig == unix.SIGSTOP { 197 // SIGSTOP was delivered to another thread in the same thread 198 // group, which initiated another group stop. Just ignore it. 199 continue 200 } 201 if sig == (syscallEvent | unix.SIGTRAP) { 202 t.dumpAndPanic(fmt.Sprintf("unexpected syscall event")) 203 } 204 break 205 } 206 if err := t.getRegs(regs); err != nil { 207 panic(fmt.Sprintf("ptrace get regs failed: %v", err)) 208 } 209 }