github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/platform/systrap/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 //go:build arm64 16 // +build arm64 17 18 package systrap 19 20 import ( 21 "fmt" 22 "strings" 23 24 "golang.org/x/sys/unix" 25 "github.com/metacubex/gvisor/pkg/abi/linux" 26 "github.com/metacubex/gvisor/pkg/seccomp" 27 "github.com/metacubex/gvisor/pkg/sentry/arch" 28 "github.com/metacubex/gvisor/pkg/sentry/platform/systrap/sysmsg" 29 ) 30 31 const ( 32 // initRegsRipAdjustment is the size of the svc instruction. 33 initRegsRipAdjustment = 4 34 ) 35 36 // resetSysemuRegs sets up emulation registers. 37 // 38 // This should be called prior to calling sysemu. 39 func (s *subprocess) resetSysemuRegs(regs *arch.Registers) { 40 } 41 42 // createSyscallRegs sets up syscall registers. 43 // 44 // This should be called to generate registers for a system call. 45 func createSyscallRegs(initRegs *arch.Registers, sysno uintptr, args ...arch.SyscallArgument) arch.Registers { 46 // Copy initial registers (Pc, Sp, etc.). 47 regs := *initRegs 48 49 // Set our syscall number. 50 // r8 for the syscall number. 51 // r0-r6 is used to store the parameters. 52 regs.Regs[8] = uint64(sysno) 53 if len(args) >= 1 { 54 regs.Regs[0] = args[0].Uint64() 55 } 56 if len(args) >= 2 { 57 regs.Regs[1] = args[1].Uint64() 58 } 59 if len(args) >= 3 { 60 regs.Regs[2] = args[2].Uint64() 61 } 62 if len(args) >= 4 { 63 regs.Regs[3] = args[3].Uint64() 64 } 65 if len(args) >= 5 { 66 regs.Regs[4] = args[4].Uint64() 67 } 68 if len(args) >= 6 { 69 regs.Regs[5] = args[5].Uint64() 70 } 71 72 return regs 73 } 74 75 // updateSyscallRegs updates registers after finishing sysemu. 76 func updateSyscallRegs(regs *arch.Registers) { 77 // No special work is necessary. 78 return 79 } 80 81 // syscallReturnValue extracts a sensible return from registers. 82 func syscallReturnValue(regs *arch.Registers) (uintptr, error) { 83 rval := int64(regs.Regs[0]) 84 if rval < 0 { 85 return 0, unix.Errno(-rval) 86 } 87 return uintptr(rval), nil 88 } 89 90 func dumpRegs(regs *arch.Registers) string { 91 var m strings.Builder 92 93 fmt.Fprintf(&m, "Registers:\n") 94 95 for i := 0; i < 31; i++ { 96 fmt.Fprintf(&m, "\tRegs[%d]\t = %016x\n", i, regs.Regs[i]) 97 } 98 fmt.Fprintf(&m, "\tSp\t = %016x\n", regs.Sp) 99 fmt.Fprintf(&m, "\tPc\t = %016x\n", regs.Pc) 100 fmt.Fprintf(&m, "\tPstate\t = %016x\n", regs.Pstate) 101 102 return m.String() 103 } 104 105 // adjustInitregsRip adjust the current register RIP value to 106 // be just before the system call instruction execution. 107 func (t *thread) adjustInitRegsRip() { 108 t.initRegs.Pc -= initRegsRipAdjustment 109 } 110 111 // Pass the expected PPID to the child via X7 when creating stub process 112 func initChildProcessPPID(initregs *arch.Registers, ppid int32) { 113 // R9 has to be set to 1 when creating stub process. 114 initregs.Regs[9] = _NEW_STUB 115 } 116 117 func maybePatchSignalInfo(regs *arch.Registers, signalInfo *linux.SignalInfo) (patched bool) { 118 // vsyscall emulation is not supported on ARM64. No need to patch anything. 119 return false 120 } 121 122 // Noop on arm64. 123 // 124 //go:nosplit 125 func enableCpuidFault() { 126 } 127 128 // appendArchSeccompRules append architecture specific seccomp rules when creating BPF program. 129 // Ref attachedThread() for more detail. 130 func appendArchSeccompRules(rules []seccomp.RuleSet) []seccomp.RuleSet { 131 return rules 132 } 133 134 // probeSeccomp returns true if seccomp is run after ptrace notifications, 135 // which is generally the case for kernel version >= 4.8. 136 // 137 // On arm64, the support of PTRACE_SYSEMU was added in the 5.3 kernel, so 138 // probeSeccomp can always return true. 139 func probeSeccomp() bool { 140 return true 141 } 142 143 func (s *subprocess) arm64SyscallWorkaround(t *thread, regs *arch.Registers) { 144 // On ARM64, when ptrace stops on a system call, it uses the x7 145 // register to indicate whether the stop has been signalled from 146 // syscall entry or syscall exit. This means that we can't get a value 147 // of this register and we can't change it. More details are in the 148 // comment for tracehook_report_syscall in arch/arm64/kernel/ptrace.c. 149 // 150 // This happens only if we stop on a system call, so let's queue a 151 // signal, resume a stub thread and catch it on a signal handling. 152 t.NotifyInterrupt() 153 for { 154 if _, _, errno := unix.RawSyscall6( 155 unix.SYS_PTRACE, 156 unix.PTRACE_SYSEMU, 157 uintptr(t.tid), 0, 0, 0, 0); errno != 0 { 158 panic(fmt.Sprintf("ptrace sysemu failed: %v", errno)) 159 } 160 161 // Wait for the syscall-enter stop. 162 sig := t.wait(stopped) 163 if sig == unix.SIGSTOP { 164 // SIGSTOP was delivered to another thread in the same thread 165 // group, which initiated another group stop. Just ignore it. 166 continue 167 } 168 if sig == (syscallEvent | unix.SIGTRAP) { 169 t.dumpAndPanic(fmt.Sprintf("unexpected syscall event")) 170 } 171 break 172 } 173 if err := t.getRegs(regs); err != nil { 174 panic(fmt.Sprintf("ptrace get regs failed: %v", err)) 175 } 176 } 177 178 func restoreArchSpecificState(ctx *sysmsg.ThreadContext, ac *arch.Context64) { 179 ctx.TLS = uint64(ac.TLS()) 180 } 181 182 func setArchSpecificRegs(sysThread *sysmsgThread, regs *arch.Registers) { 183 } 184 185 func retrieveArchSpecificState(ctx *sysmsg.ThreadContext, ac *arch.Context64) { 186 if !ac.SetTLS(uintptr(ctx.TLS)) { 187 panic(fmt.Sprintf("ac.SetTLS(%+v) failed", ctx.TLS)) 188 } 189 } 190 191 func archSpecificSysmsgThreadInit(sysThread *sysmsgThread) { 192 // Send a fake event to stop the BPF process so that it enters the sighandler. 193 if _, _, e := unix.RawSyscall(unix.SYS_TGKILL, uintptr(sysThread.thread.tgid), uintptr(sysThread.thread.tid), uintptr(unix.SIGSEGV)); e != 0 { 194 panic(fmt.Sprintf("tkill failed: %v", e)) 195 } 196 }