gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/platform/systrap/subprocess_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 go1.18
    16  // +build go1.18
    17  
    18  // //go:linkname directives type-checked by checklinkname. Any other
    19  // non-linkname assumptions outside the Go 1 compatibility guarantee should
    20  // have an accompanied vet check or version guard build tag.
    21  
    22  package systrap
    23  
    24  import (
    25  	"fmt"
    26  	"unsafe"
    27  
    28  	"golang.org/x/sys/unix"
    29  	"gvisor.dev/gvisor/pkg/abi/linux"
    30  	"gvisor.dev/gvisor/pkg/log"
    31  	"gvisor.dev/gvisor/pkg/sentry/arch"
    32  	"gvisor.dev/gvisor/pkg/sentry/memmap"
    33  	"gvisor.dev/gvisor/pkg/sentry/pgalloc"
    34  	"gvisor.dev/gvisor/pkg/sentry/platform/systrap/sysmsg"
    35  )
    36  
    37  //go:linkname beforeFork syscall.runtime_BeforeFork
    38  func beforeFork()
    39  
    40  //go:linkname afterFork syscall.runtime_AfterFork
    41  func afterFork()
    42  
    43  //go:linkname afterForkInChild syscall.runtime_AfterForkInChild
    44  func afterForkInChild()
    45  
    46  // cputicks is implemented in assembly.
    47  func cputicks() int64
    48  
    49  // spinloop is implemented in assembly.
    50  func spinloop()
    51  
    52  // getThreadContextFromID returns a ThreadContext struct that corresponds to the
    53  // given ID.
    54  //
    55  // Precondition: cid must be a valid thread context ID that has a mapping for it
    56  // that exists in s.contexts.
    57  func (s *subprocess) getThreadContextFromID(cid uint64) *sysmsg.ThreadContext {
    58  	tcSlot := s.threadContextRegion + uintptr(cid)*sysmsg.AllocatedSizeofThreadContextStruct
    59  	return (*sysmsg.ThreadContext)(unsafe.Pointer(tcSlot))
    60  }
    61  
    62  func mmapContextQueueForSentry(memoryFile *pgalloc.MemoryFile, opts pgalloc.AllocOpts) (memmap.FileRange, *contextQueue) {
    63  	fr, err := memoryFile.Allocate(uint64(stubContextQueueRegionLen), opts)
    64  	if err != nil {
    65  		panic(fmt.Sprintf("failed to allocate a new subprocess context memory region"))
    66  	}
    67  	addr, _, errno := unix.RawSyscall6(
    68  		unix.SYS_MMAP,
    69  		0,
    70  		uintptr(fr.Length()),
    71  		unix.PROT_WRITE|unix.PROT_READ,
    72  		unix.MAP_SHARED|unix.MAP_FILE,
    73  		uintptr(memoryFile.FD()), uintptr(fr.Start))
    74  	if errno != 0 {
    75  		panic(fmt.Sprintf("mmap failed for subprocess context memory region: %v", errno))
    76  	}
    77  
    78  	return fr, (*contextQueue)(unsafe.Pointer(addr))
    79  }
    80  
    81  func saveFPState(ctx *sharedContext, ac *arch.Context64) {
    82  	fpState := ac.FloatingPointData().BytePointer()
    83  	dst := unsafeSlice(uintptr(unsafe.Pointer(fpState)), archState.FpLen())
    84  	src := ctx.shared.FPState[:]
    85  	copy(dst, src)
    86  }
    87  
    88  // restoreFPStateDecoupledContext writes FPState from c to the thread context
    89  // shared memory region if there is any need to do so.
    90  func restoreFPState(ctx *sharedContext, c *platformContext, ac *arch.Context64) {
    91  	if !c.needRestoreFPState {
    92  		return
    93  	}
    94  	c.needRestoreFPState = false
    95  	ctx.setFPStateChanged()
    96  
    97  	fpState := ac.FloatingPointData().BytePointer()
    98  	src := unsafeSlice(uintptr(unsafe.Pointer(fpState)), archState.FpLen())
    99  	dst := ctx.shared.FPState[:]
   100  	copy(dst, src)
   101  }
   102  
   103  // alive returns true if the subprocess is alive.
   104  func (s *subprocess) alive() bool {
   105  	if s.dead.Load() {
   106  		return false
   107  	}
   108  
   109  	// Wait4 doesn't support WNOWAIT, but here is no other way to find out
   110  	// whether a process exited or was stopped by ptrace.
   111  	siginfo := linux.SignalInfo{}
   112  	_, _, errno := unix.Syscall6(
   113  		unix.SYS_WAITID,
   114  		unix.P_PID,
   115  		uintptr(s.syscallThread.thread.tid),
   116  		uintptr(unsafe.Pointer(&siginfo)),
   117  		uintptr(unix.WEXITED|unix.WNOHANG|unix.WNOWAIT),
   118  		0, 0)
   119  	if errno == 0 && siginfo.PID() == 0 {
   120  		return true
   121  	}
   122  	if errno == 0 && siginfo.Code != linux.CLD_EXITED && siginfo.Code != linux.CLD_KILLED {
   123  		return true
   124  	}
   125  
   126  	// The process is dead, let's collect its zombie.
   127  	wstatus := unix.WaitStatus(0)
   128  	pid, err := unix.Wait4(int(s.syscallThread.thread.tid), &wstatus, unix.WNOHANG, nil)
   129  	log.Warningf("the subprocess %d exited (status: %s, err %s)", pid, wstatus, err)
   130  	s.dead.Store(true)
   131  	return false
   132  }