github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/sentry/platform/kvm/bluepill_amd64_unsafe.go (about) 1 // Copyright 2018 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 amd64 16 // +build amd64 17 18 package kvm 19 20 import ( 21 "unsafe" 22 23 "github.com/ttpreport/gvisor-ligolo/pkg/ring0" 24 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/arch" 25 "golang.org/x/sys/unix" 26 ) 27 28 // dieArchSetup initializes the state for dieTrampoline. 29 // 30 // The amd64 dieTrampoline requires the vCPU to be set in BX, and the last RIP 31 // to be in AX. The trampoline then simulates a call to dieHandler from the 32 // provided RIP. 33 // 34 //go:nosplit 35 func dieArchSetup(c *vCPU, context *arch.SignalContext64, guestRegs *userRegs) { 36 // Reload all registers to have an accurate stack trace when we return 37 // to host mode. This means that the stack should be unwound correctly. 38 if errno := c.getUserRegisters(&c.dieState.guestRegs); errno != 0 { 39 throw(c.dieState.message) 40 } 41 42 // If the vCPU is in user mode, we set the stack to the stored stack 43 // value in the vCPU itself. We don't want to unwind the user stack. 44 if guestRegs.RFLAGS&ring0.UserFlagsSet == ring0.UserFlagsSet { 45 regs := c.CPU.Registers() 46 context.Rax = regs.Rax 47 context.Rsp = regs.Rsp 48 context.Rbp = regs.Rbp 49 } else { 50 context.Rax = guestRegs.RIP 51 context.Rsp = guestRegs.RSP 52 context.Rbp = guestRegs.RBP 53 context.Eflags = guestRegs.RFLAGS 54 } 55 context.Rbx = uint64(uintptr(unsafe.Pointer(c))) 56 context.Rip = uint64(dieTrampolineAddr) 57 } 58 59 // getHypercallID returns hypercall ID. 60 // 61 //go:nosplit 62 func getHypercallID(addr uintptr) int { 63 return _KVM_HYPERCALL_MAX 64 } 65 66 // bluepillStopGuest is reponsible for injecting interrupt. 67 // 68 //go:nosplit 69 func bluepillStopGuest(c *vCPU) { 70 // Interrupt: we must have requested an interrupt 71 // window; set the interrupt line. 72 if _, _, errno := unix.RawSyscall( 73 unix.SYS_IOCTL, 74 uintptr(c.fd), 75 _KVM_INTERRUPT, 76 uintptr(unsafe.Pointer(&bounce))); errno != 0 { 77 throw("interrupt injection failed") 78 } 79 // Clear previous injection request. 80 c.runData.requestInterruptWindow = 0 81 } 82 83 // bluepillSigBus is reponsible for injecting NMI to trigger sigbus. 84 // 85 //go:nosplit 86 func bluepillSigBus(c *vCPU) { 87 if _, _, errno := unix.RawSyscall( // escapes: no. 88 unix.SYS_IOCTL, 89 uintptr(c.fd), 90 _KVM_NMI, 0); errno != 0 { 91 throw("NMI injection failed") 92 } 93 } 94 95 // bluepillHandleEnosys is reponsible for handling enosys error. 96 // 97 //go:nosplit 98 func bluepillHandleEnosys(c *vCPU) { 99 throw("run failed: ENOSYS") 100 } 101 102 // bluepillReadyStopGuest checks whether the current vCPU is ready for interrupt injection. 103 // 104 //go:nosplit 105 func bluepillReadyStopGuest(c *vCPU) bool { 106 if c.runData.readyForInterruptInjection == 0 { 107 return false 108 } 109 110 if c.runData.ifFlag == 0 { 111 // This is impossible if readyForInterruptInjection is 1. 112 throw("interrupts are disabled") 113 } 114 115 // Disable interrupts if we are in the kernel space. 116 // 117 // When the Sentry switches into the kernel mode, it disables 118 // interrupts. But when goruntime switches on a goroutine which has 119 // been saved in the host mode, it restores flags and this enables 120 // interrupts. See the comment of UserFlagsSet for more details. 121 uregs := userRegs{} 122 err := c.getUserRegisters(&uregs) 123 if err != 0 { 124 throw("failed to get user registers") 125 } 126 127 if ring0.IsKernelFlags(uregs.RFLAGS) { 128 uregs.RFLAGS &^= ring0.KernelFlagsClear 129 err = c.setUserRegisters(&uregs) 130 if err != 0 { 131 throw("failed to set user registers") 132 } 133 return false 134 } 135 return true 136 } 137 138 // bluepillArchHandleExit checks architecture specific exitcode. 139 // 140 //go:nosplit 141 func bluepillArchHandleExit(c *vCPU, context unsafe.Pointer) { 142 c.die(bluepillArchContext(context), "unknown") 143 }