github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/fsimpl/devpts/queue.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 package devpts 16 17 import ( 18 "github.com/nicocha30/gvisor-ligolo/pkg/abi/linux" 19 "github.com/nicocha30/gvisor-ligolo/pkg/context" 20 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 21 "github.com/nicocha30/gvisor-ligolo/pkg/marshal/primitive" 22 "github.com/nicocha30/gvisor-ligolo/pkg/safemem" 23 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/arch" 24 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel" 25 "github.com/nicocha30/gvisor-ligolo/pkg/sync" 26 "github.com/nicocha30/gvisor-ligolo/pkg/usermem" 27 "github.com/nicocha30/gvisor-ligolo/pkg/waiter" 28 ) 29 30 // waitBufMaxBytes is the maximum size of a wait buffer. It is based on 31 // TTYB_DEFAULT_MEM_LIMIT. 32 const waitBufMaxBytes = 131072 33 34 // queue represents one of the input or output queues between a pty master and 35 // replica. Bytes written to a queue are added to the read buffer until it is 36 // full, at which point they are written to the wait buffer. Bytes are 37 // processed (i.e. undergo termios transformations) as they are added to the 38 // read buffer. The read buffer is readable when its length is nonzero and 39 // readable is true. 40 // 41 // +stateify savable 42 type queue struct { 43 // mu protects everything in queue. 44 mu sync.Mutex `state:"nosave"` 45 46 // readBuf is buffer of data ready to be read when readable is true. 47 // This data has been processed. 48 readBuf []byte 49 50 // waitBuf contains data that can't fit into readBuf. It is put here 51 // until it can be loaded into the read buffer. waitBuf contains data 52 // that hasn't been processed. 53 waitBuf [][]byte 54 waitBufLen uint64 55 56 // readable indicates whether the read buffer can be read from. In 57 // canonical mode, there can be an unterminated line in the read buffer, 58 // so readable must be checked. 59 readable bool 60 61 // transform is the the queue's function for transforming bytes 62 // entering the queue. For example, transform might convert all '\r's 63 // entering the queue to '\n's. 64 transformer 65 } 66 67 // readReadiness returns whether q is ready to be read from. 68 func (q *queue) readReadiness(t *linux.KernelTermios) waiter.EventMask { 69 q.mu.Lock() 70 defer q.mu.Unlock() 71 if len(q.readBuf) > 0 && q.readable { 72 return waiter.ReadableEvents 73 } 74 return waiter.EventMask(0) 75 } 76 77 // writeReadiness returns whether q is ready to be written to. 78 func (q *queue) writeReadiness(t *linux.KernelTermios) waiter.EventMask { 79 q.mu.Lock() 80 defer q.mu.Unlock() 81 if q.waitBufLen < waitBufMaxBytes { 82 return waiter.WritableEvents 83 } 84 return waiter.EventMask(0) 85 } 86 87 // readableSize writes the number of readable bytes to userspace. 88 func (q *queue) readableSize(t *kernel.Task, io usermem.IO, args arch.SyscallArguments) error { 89 q.mu.Lock() 90 defer q.mu.Unlock() 91 size := primitive.Int32(0) 92 if q.readable { 93 size = primitive.Int32(len(q.readBuf)) 94 } 95 96 _, err := size.CopyOut(t, args[2].Pointer()) 97 return err 98 99 } 100 101 // read reads from q to userspace. It returns: 102 // - The number of bytes read 103 // - Whether the read caused more readable data to become available (whether 104 // data was pushed from the wait buffer to the read buffer). 105 // - Whether any data was echoed back (need to notify readers). 106 // 107 // Preconditions: l.termiosMu must be held for reading. 108 func (q *queue) read(ctx context.Context, dst usermem.IOSequence, l *lineDiscipline) (int64, bool, bool, error) { 109 q.mu.Lock() 110 defer q.mu.Unlock() 111 112 if !q.readable { 113 if l.numReplicas == 0 { 114 return 0, false, false, linuxerr.EIO 115 } 116 return 0, false, false, linuxerr.ErrWouldBlock 117 } 118 119 if dst.NumBytes() > canonMaxBytes { 120 dst = dst.TakeFirst(canonMaxBytes) 121 } 122 123 n, err := dst.CopyOutFrom(ctx, safemem.ReaderFunc(func(dst safemem.BlockSeq) (uint64, error) { 124 src := safemem.BlockSeqOf(safemem.BlockFromSafeSlice(q.readBuf)) 125 n, err := safemem.CopySeq(dst, src) 126 if err != nil { 127 return 0, err 128 } 129 q.readBuf = q.readBuf[n:] 130 131 // If we read everything, this queue is no longer readable. 132 if len(q.readBuf) == 0 { 133 q.readable = false 134 } 135 136 return n, nil 137 })) 138 if err != nil { 139 return 0, false, false, err 140 } 141 142 // Move data from the queue's wait buffer to its read buffer. 143 nPushed, notifyEcho := q.pushWaitBufLocked(l) 144 145 return int64(n), nPushed > 0, notifyEcho, nil 146 } 147 148 // write writes to q from userspace. 149 // The returned boolean indicates whether any data was echoed back. 150 // 151 // Preconditions: l.termiosMu must be held for reading. 152 func (q *queue) write(ctx context.Context, src usermem.IOSequence, l *lineDiscipline) (int64, bool, error) { 153 q.mu.Lock() 154 defer q.mu.Unlock() 155 156 // Copy data into the wait buffer. 157 n, err := src.CopyInTo(ctx, safemem.WriterFunc(func(src safemem.BlockSeq) (uint64, error) { 158 copyLen := src.NumBytes() 159 room := waitBufMaxBytes - q.waitBufLen 160 // If out of room, return EAGAIN. 161 if room == 0 && copyLen > 0 { 162 return 0, linuxerr.ErrWouldBlock 163 } 164 // Cap the size of the wait buffer. 165 if copyLen > room { 166 copyLen = room 167 src = src.TakeFirst64(room) 168 } 169 buf := make([]byte, copyLen) 170 171 // Copy the data into the wait buffer. 172 dst := safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf)) 173 n, err := safemem.CopySeq(dst, src) 174 if err != nil { 175 return 0, err 176 } 177 q.waitBufAppend(buf) 178 179 return n, nil 180 })) 181 if err != nil { 182 return 0, false, err 183 } 184 185 // Push data from the wait to the read buffer. 186 _, notifyEcho := q.pushWaitBufLocked(l) 187 188 return n, notifyEcho, nil 189 } 190 191 // writeBytes writes to q from b. 192 // The returned boolean indicates whether any data was echoed back. 193 // 194 // Preconditions: l.termiosMu must be held for reading. 195 func (q *queue) writeBytes(b []byte, l *lineDiscipline) bool { 196 q.mu.Lock() 197 defer q.mu.Unlock() 198 199 // Write to the wait buffer. 200 q.waitBufAppend(b) 201 _, notifyEcho := q.pushWaitBufLocked(l) 202 return notifyEcho 203 } 204 205 // pushWaitBufLocked fills the queue's read buffer with data from the wait 206 // buffer. 207 // The returned boolean indicates whether any data was echoed back. 208 // 209 // Preconditions: 210 // - l.termiosMu must be held for reading. 211 // - q.mu must be locked. 212 func (q *queue) pushWaitBufLocked(l *lineDiscipline) (int, bool) { 213 if q.waitBufLen == 0 { 214 return 0, false 215 } 216 217 // Move data from the wait to the read buffer. 218 var total int 219 var i int 220 var notifyEcho bool 221 for i = 0; i < len(q.waitBuf); i++ { 222 n, echo := q.transform(l, q, q.waitBuf[i]) 223 total += n 224 notifyEcho = notifyEcho || echo 225 if n != len(q.waitBuf[i]) { 226 // The read buffer filled up without consuming the 227 // entire buffer. 228 q.waitBuf[i] = q.waitBuf[i][n:] 229 break 230 } 231 } 232 233 // Update wait buffer based on consumed data. 234 q.waitBuf = q.waitBuf[i:] 235 q.waitBufLen -= uint64(total) 236 237 return total, notifyEcho 238 } 239 240 // Precondition: q.mu must be locked. 241 func (q *queue) waitBufAppend(b []byte) { 242 q.waitBuf = append(q.waitBuf, b) 243 q.waitBufLen += uint64(len(b)) 244 }