github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/syscalls/linux/sys_process_vm.go (about)

     1  // Copyright 2020 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  package linux
    16  
    17  import (
    18  	"github.com/nicocha30/gvisor-ligolo/pkg/abi/linux"
    19  	"github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr"
    20  	"github.com/nicocha30/gvisor-ligolo/pkg/hostarch"
    21  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/arch"
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel"
    23  	"github.com/nicocha30/gvisor-ligolo/pkg/usermem"
    24  )
    25  
    26  type vmReadWriteOp int
    27  
    28  const (
    29  	localReadLocalWrite vmReadWriteOp = iota
    30  	remoteReadLocalWrite
    31  	localReadRemoteWrite
    32  )
    33  
    34  // ProcessVMReadv implements process_vm_readv(2).
    35  func ProcessVMReadv(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    36  	return processVMRW(t, args, false /*isWrite*/)
    37  }
    38  
    39  // ProcessVMWritev implements process_vm_writev(2).
    40  func ProcessVMWritev(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    41  	return processVMRW(t, args, true /*isWrite*/)
    42  }
    43  
    44  func processVMRW(t *kernel.Task, args arch.SyscallArguments, isWrite bool) (uintptr, *kernel.SyscallControl, error) {
    45  	pid := kernel.ThreadID(args[0].Int())
    46  	lvec := hostarch.Addr(args[1].Pointer())
    47  	liovcnt := int(args[2].Int64())
    48  	rvec := hostarch.Addr(args[3].Pointer())
    49  	riovcnt := int(args[4].Int64())
    50  	flags := args[5].Int()
    51  
    52  	switch {
    53  	case flags != 0 ||
    54  		liovcnt < 0 ||
    55  		riovcnt < 0 ||
    56  		liovcnt > linux.UIO_MAXIOV ||
    57  		riovcnt > linux.UIO_MAXIOV:
    58  		return 0, nil, linuxerr.EINVAL
    59  	case lvec == 0 || rvec == 0:
    60  		return 0, nil, linuxerr.EFAULT
    61  	case liovcnt == 0 || riovcnt == 0:
    62  		return 0, nil, nil
    63  	}
    64  
    65  	localProcess := t.ThreadGroup().Leader()
    66  	if localProcess == nil {
    67  		return 0, nil, linuxerr.ESRCH
    68  	}
    69  	remoteThreadGroup := localProcess.PIDNamespace().ThreadGroupWithID(pid)
    70  	if remoteThreadGroup == nil {
    71  		return 0, nil, linuxerr.ESRCH
    72  	}
    73  	remoteProcess := remoteThreadGroup.Leader()
    74  
    75  	isRemote := localProcess == remoteProcess
    76  
    77  	// For the write case, we read from the local process and write to the remote process.
    78  	op := localReadLocalWrite
    79  	if isWrite {
    80  		if isRemote {
    81  			op = remoteReadLocalWrite
    82  		}
    83  		return doProcessVMReadWrite(localProcess, remoteProcess, lvec, rvec, liovcnt, riovcnt, op)
    84  	}
    85  	// For the read case, we read from the remote process and write to the local process.
    86  	if isRemote {
    87  		op = localReadRemoteWrite
    88  	}
    89  	return doProcessVMReadWrite(remoteProcess, localProcess, rvec, lvec, riovcnt, liovcnt, op)
    90  }
    91  
    92  func doProcessVMReadWrite(rProcess, wProcess *kernel.Task, rAddr, wAddr hostarch.Addr, rIovecCount, wIovecCount int, op vmReadWriteOp) (uintptr, *kernel.SyscallControl, error) {
    93  	rCtx := rProcess.CopyContext(rProcess, usermem.IOOpts{})
    94  	wCtx := wProcess.CopyContext(wProcess, usermem.IOOpts{})
    95  
    96  	var wCount int
    97  	doProcessVMReadWriteMaybeLocked := func() error {
    98  		rIovecs, err := rCtx.CopyInIovecs(rAddr, rIovecCount)
    99  		if err != nil {
   100  			return err
   101  		}
   102  		wIovecs, err := wCtx.CopyInIovecs(wAddr, wIovecCount)
   103  		if err != nil {
   104  			return err
   105  		}
   106  
   107  		bufSize := 0
   108  		for _, rIovec := range rIovecs {
   109  			if int(rIovec.Length()) > bufSize {
   110  				bufSize = int(rIovec.Length())
   111  			}
   112  		}
   113  
   114  		var buf []byte
   115  		// We need to copy the called task's scratch buffer so we don't get a data race. If we are
   116  		// reading a remote process's memory, then we are on the writer's task goroutine, so use
   117  		// the write context's scratch buffer.
   118  		if op == remoteReadLocalWrite {
   119  			buf = wCtx.CopyScratchBuffer(bufSize)
   120  		} else {
   121  			buf = rCtx.CopyScratchBuffer(bufSize)
   122  		}
   123  
   124  		for _, rIovec := range rIovecs {
   125  			if len(wIovecs) <= 0 {
   126  				break
   127  			}
   128  
   129  			buf = buf[0:int(rIovec.Length())]
   130  			bytes, err := rCtx.CopyInBytes(rIovec.Start, buf)
   131  			if linuxerr.Equals(linuxerr.EFAULT, err) {
   132  				return nil
   133  			}
   134  			if err != nil {
   135  				return err
   136  			}
   137  			if bytes != int(rIovec.Length()) {
   138  				return nil
   139  			}
   140  			start := 0
   141  			for bytes > start && 0 < len(wIovecs) {
   142  				writeLength := int(wIovecs[0].Length())
   143  				if writeLength > (bytes - start) {
   144  					writeLength = bytes - start
   145  				}
   146  				out, err := wCtx.CopyOutBytes(wIovecs[0].Start, buf[start:writeLength+start])
   147  				wCount += out
   148  				start += out
   149  				if linuxerr.Equals(linuxerr.EFAULT, err) {
   150  					return nil
   151  				}
   152  				if err != nil {
   153  					return err
   154  				}
   155  				if out != writeLength {
   156  					return nil
   157  				}
   158  				wIovecs[0].Start += hostarch.Addr(out)
   159  				if !wIovecs[0].WellFormed() {
   160  					return err
   161  				}
   162  				if wIovecs[0].Length() == 0 {
   163  					wIovecs = wIovecs[1:]
   164  				}
   165  			}
   166  		}
   167  		return nil
   168  	}
   169  
   170  	var err error
   171  
   172  	switch op {
   173  	case remoteReadLocalWrite:
   174  		err = rCtx.WithTaskMutexLocked(doProcessVMReadWriteMaybeLocked)
   175  	case localReadRemoteWrite:
   176  		err = wCtx.WithTaskMutexLocked(doProcessVMReadWriteMaybeLocked)
   177  
   178  	case localReadLocalWrite:
   179  		// in the case of local reads/writes, we don't have to lock the task mutex, because we are
   180  		// running on the top of the task's goroutine already.
   181  		err = doProcessVMReadWriteMaybeLocked()
   182  	default:
   183  		panic("unsupported operation passed")
   184  	}
   185  
   186  	return uintptr(wCount), nil, err
   187  }