github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/safemem/seq_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 package safemem 16 17 import ( 18 "bytes" 19 "fmt" 20 "unsafe" 21 22 "golang.org/x/sys/unix" 23 "github.com/nicocha30/gvisor-ligolo/pkg/gohacks" 24 ) 25 26 // A BlockSeq represents a sequence of Blocks, each of which has non-zero 27 // length. 28 // 29 // BlockSeqs are immutable and may be copied by value. The zero value of 30 // BlockSeq represents an empty sequence. 31 type BlockSeq struct { 32 // If length is 0, then the BlockSeq is empty. Invariants: data == 0; 33 // offset == 0; limit == 0. 34 // 35 // If length is -1, then the BlockSeq represents the single Block{data, 36 // limit, false}. Invariants: offset == 0; limit > 0; limit does not 37 // overflow the range of an int. 38 // 39 // If length is -2, then the BlockSeq represents the single Block{data, 40 // limit, true}. Invariants: offset == 0; limit > 0; limit does not 41 // overflow the range of an int. 42 // 43 // Otherwise, length >= 2, and the BlockSeq represents the `length` Blocks 44 // in the array of Blocks starting at address `data`, starting at `offset` 45 // bytes into the first Block and limited to the following `limit` bytes. 46 // Invariants: data != 0; offset < len(data[0]); limit > 0; offset+limit <= 47 // the combined length of all Blocks in the array; the first Block in the 48 // array has non-zero length. 49 // 50 // length is never 1; sequences consisting of a single Block are always 51 // stored inline (with length < 0). 52 data unsafe.Pointer 53 length int 54 offset int 55 limit uint64 56 } 57 58 // BlockSeqOf returns a BlockSeq representing the single Block b. 59 func BlockSeqOf(b Block) BlockSeq { 60 if b.length == 0 { 61 return BlockSeq{} 62 } 63 bs := BlockSeq{ 64 data: b.start, 65 length: -1, 66 limit: uint64(b.length), 67 } 68 if b.needSafecopy { 69 bs.length = -2 70 } 71 return bs 72 } 73 74 // BlockSeqFromSlice returns a BlockSeq representing all Blocks in slice. 75 // If slice contains Blocks with zero length, BlockSeq will skip them during 76 // iteration. 77 // 78 // Whether the returned BlockSeq shares memory with slice is unspecified; 79 // clients should avoid mutating slices passed to BlockSeqFromSlice. 80 // 81 // Preconditions: The combined length of all Blocks in slice <= math.MaxUint64. 82 func BlockSeqFromSlice(slice []Block) BlockSeq { 83 slice = skipEmpty(slice) 84 var limit uint64 85 for _, b := range slice { 86 sum := limit + uint64(b.Len()) 87 if sum < limit { 88 panic("BlockSeq length overflows uint64") 89 } 90 limit = sum 91 } 92 return blockSeqFromSliceLimited(slice, limit) 93 } 94 95 // Preconditions: 96 // - The combined length of all Blocks in slice <= limit. 97 // - If len(slice) != 0, the first Block in slice has non-zero length and 98 // limit > 0. 99 func blockSeqFromSliceLimited(slice []Block, limit uint64) BlockSeq { 100 switch len(slice) { 101 case 0: 102 return BlockSeq{} 103 case 1: 104 return BlockSeqOf(slice[0].TakeFirst64(limit)) 105 default: 106 return BlockSeq{ 107 data: unsafe.Pointer(&slice[0]), 108 length: len(slice), 109 limit: limit, 110 } 111 } 112 } 113 114 func skipEmpty(slice []Block) []Block { 115 for i, b := range slice { 116 if b.Len() != 0 { 117 return slice[i:] 118 } 119 } 120 return nil 121 } 122 123 // IsEmpty returns true if bs contains no Blocks. 124 // 125 // Invariants: bs.IsEmpty() == (bs.NumBlocks() == 0) == (bs.NumBytes() == 0). 126 // (Of these, prefer to use bs.IsEmpty().) 127 func (bs BlockSeq) IsEmpty() bool { 128 return bs.length == 0 129 } 130 131 // NumBlocks returns the number of Blocks in bs. 132 func (bs BlockSeq) NumBlocks() int { 133 // In general, we have to count: if bs represents a windowed slice then the 134 // slice may contain Blocks with zero length, and bs.length may be larger 135 // than the actual number of Blocks due to bs.limit. 136 var n int 137 for !bs.IsEmpty() { 138 n++ 139 bs = bs.Tail() 140 } 141 return n 142 } 143 144 // NumBytes returns the sum of Block.Len() for all Blocks in bs. 145 func (bs BlockSeq) NumBytes() uint64 { 146 return bs.limit 147 } 148 149 // Head returns the first Block in bs. 150 // 151 // Preconditions: !bs.IsEmpty(). 152 func (bs BlockSeq) Head() Block { 153 if bs.length == 0 { 154 panic("empty BlockSeq") 155 } 156 if bs.length < 0 { 157 return bs.internalBlock() 158 } 159 return (*Block)(bs.data).DropFirst(bs.offset).TakeFirst64(bs.limit) 160 } 161 162 // Preconditions: bs.length < 0. 163 func (bs BlockSeq) internalBlock() Block { 164 return Block{ 165 start: bs.data, 166 length: int(bs.limit), 167 needSafecopy: bs.length == -2, 168 } 169 } 170 171 // Tail returns a BlockSeq consisting of all Blocks in bs after the first. 172 // 173 // Preconditions: !bs.IsEmpty(). 174 func (bs BlockSeq) Tail() BlockSeq { 175 if bs.length == 0 { 176 panic("empty BlockSeq") 177 } 178 if bs.length < 0 { 179 return BlockSeq{} 180 } 181 data := (*Block)(bs.data) 182 head := data.DropFirst(bs.offset) 183 headLen := uint64(head.Len()) 184 if headLen >= bs.limit { 185 // The head Block exhausts the limit, so the tail is empty. 186 return BlockSeq{} 187 } 188 extSlice := gohacks.Slice(data, bs.length) 189 tailSlice := skipEmpty(extSlice[1:]) 190 tailLimit := bs.limit - headLen 191 return blockSeqFromSliceLimited(tailSlice, tailLimit) 192 } 193 194 // DropFirst returns a BlockSeq equivalent to bs, but with the first n bytes 195 // omitted. If n > bs.NumBytes(), DropFirst returns an empty BlockSeq. 196 // 197 // Preconditions: n >= 0. 198 func (bs BlockSeq) DropFirst(n int) BlockSeq { 199 if n < 0 { 200 panic(fmt.Sprintf("invalid n: %d", n)) 201 } 202 return bs.DropFirst64(uint64(n)) 203 } 204 205 // DropFirst64 is equivalent to DropFirst but takes an uint64. 206 func (bs BlockSeq) DropFirst64(n uint64) BlockSeq { 207 if n >= bs.limit { 208 return BlockSeq{} 209 } 210 for { 211 // Calling bs.Head() here is surprisingly expensive, so inline getting 212 // the head's length. 213 var headLen uint64 214 if bs.length < 0 { 215 headLen = bs.limit 216 } else { 217 headLen = uint64((*Block)(bs.data).Len() - bs.offset) 218 } 219 if n < headLen { 220 // Dropping ends partway through the head Block. 221 if bs.length < 0 { 222 return BlockSeqOf(bs.internalBlock().DropFirst64(n)) 223 } 224 bs.offset += int(n) 225 bs.limit -= n 226 return bs 227 } 228 n -= headLen 229 bs = bs.Tail() 230 } 231 } 232 233 // TakeFirst returns a BlockSeq equivalent to the first n bytes of bs. If n > 234 // bs.NumBytes(), TakeFirst returns a BlockSeq equivalent to bs. 235 // 236 // Preconditions: n >= 0. 237 func (bs BlockSeq) TakeFirst(n int) BlockSeq { 238 if n < 0 { 239 panic(fmt.Sprintf("invalid n: %d", n)) 240 } 241 return bs.TakeFirst64(uint64(n)) 242 } 243 244 // TakeFirst64 is equivalent to TakeFirst but takes a uint64. 245 func (bs BlockSeq) TakeFirst64(n uint64) BlockSeq { 246 if n == 0 { 247 return BlockSeq{} 248 } 249 if bs.limit > n { 250 bs.limit = n 251 } 252 return bs 253 } 254 255 // String implements fmt.Stringer.String. 256 func (bs BlockSeq) String() string { 257 var buf bytes.Buffer 258 buf.WriteByte('[') 259 var sep string 260 for !bs.IsEmpty() { 261 buf.WriteString(sep) 262 sep = " " 263 buf.WriteString(bs.Head().String()) 264 bs = bs.Tail() 265 } 266 buf.WriteByte(']') 267 return buf.String() 268 } 269 270 // CopySeq copies srcs.NumBytes() or dsts.NumBytes() bytes, whichever is less, 271 // from srcs to dsts and returns the number of bytes copied. 272 // 273 // If srcs and dsts overlap, the data stored in dsts is unspecified. 274 func CopySeq(dsts, srcs BlockSeq) (uint64, error) { 275 var done uint64 276 for !dsts.IsEmpty() && !srcs.IsEmpty() { 277 dst := dsts.Head() 278 src := srcs.Head() 279 n, err := Copy(dst, src) 280 done += uint64(n) 281 if err != nil { 282 return done, err 283 } 284 dsts = dsts.DropFirst(n) 285 srcs = srcs.DropFirst(n) 286 } 287 return done, nil 288 } 289 290 // ZeroSeq sets all bytes in dsts to 0 and returns the number of bytes zeroed. 291 func ZeroSeq(dsts BlockSeq) (uint64, error) { 292 var done uint64 293 for !dsts.IsEmpty() { 294 n, err := Zero(dsts.Head()) 295 done += uint64(n) 296 if err != nil { 297 return done, err 298 } 299 dsts = dsts.DropFirst(n) 300 } 301 return done, nil 302 } 303 304 // IovecsFromBlockSeq returns a []unix.Iovec representing seq. 305 func IovecsFromBlockSeq(bs BlockSeq) []unix.Iovec { 306 iovs := make([]unix.Iovec, 0, bs.NumBlocks()) 307 for ; !bs.IsEmpty(); bs = bs.Tail() { 308 b := bs.Head() 309 iovs = append(iovs, unix.Iovec{ 310 Base: &b.ToSlice()[0], 311 Len: uint64(b.Len()), 312 }) 313 // We don't need to care about b.NeedSafecopy(), because the host 314 // kernel will handle such address ranges just fine (by returning 315 // EFAULT). 316 } 317 return iovs 318 }