github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/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/ttpreport/gvisor-ligolo/pkg/abi/linux" 19 "github.com/ttpreport/gvisor-ligolo/pkg/errors/linuxerr" 20 "github.com/ttpreport/gvisor-ligolo/pkg/hostarch" 21 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/arch" 22 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/kernel" 23 "github.com/ttpreport/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 }