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  }