github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/hostarch/addr_range_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 hostarch 16 17 import ( 18 "bytes" 19 "fmt" 20 "unsafe" 21 22 "github.com/nicocha30/gvisor-ligolo/pkg/gohacks" 23 ) 24 25 // An AddrRangeSeq represents a sequence of AddrRanges. 26 // 27 // AddrRangeSeqs are immutable and may be copied by value. The zero value of 28 // AddrRangeSeq represents an empty sequence. 29 // 30 // An AddrRangeSeq may contain AddrRanges with a length of 0. This is necessary 31 // since zero-length AddrRanges are significant to MM bounds checks. 32 type AddrRangeSeq struct { 33 // If length is 0, then the AddrRangeSeq represents no AddrRanges. 34 // Invariants: data == 0; offset == 0; limit == 0. 35 // 36 // If length is 1, then the AddrRangeSeq represents the single 37 // AddrRange{offset, offset+limit}. Invariants: data == 0. 38 // 39 // Otherwise, length >= 2, and the AddrRangeSeq represents the `length` 40 // AddrRanges in the array of AddrRanges starting at address `data`, 41 // starting at `offset` bytes into the first AddrRange and limited to the 42 // following `limit` bytes. (AddrRanges after `limit` are still iterated, 43 // but are truncated to a length of 0.) Invariants: data != 0; offset <= 44 // data[0].Length(); limit > 0; offset+limit <= the combined length of all 45 // AddrRanges in the array. 46 data unsafe.Pointer 47 length int 48 offset Addr 49 limit Addr 50 } 51 52 // AddrRangeSeqOf returns an AddrRangeSeq representing the single AddrRange ar. 53 func AddrRangeSeqOf(ar AddrRange) AddrRangeSeq { 54 return AddrRangeSeq{ 55 length: 1, 56 offset: ar.Start, 57 limit: ar.Length(), 58 } 59 } 60 61 // AddrRangeSeqFromSlice returns an AddrRangeSeq representing all AddrRanges in 62 // slice. 63 // 64 // Whether the returned AddrRangeSeq shares memory with slice is unspecified; 65 // clients should avoid mutating slices passed to AddrRangeSeqFromSlice. 66 // 67 // Preconditions: The combined length of all AddrRanges in slice <= 68 // math.MaxInt64. 69 func AddrRangeSeqFromSlice(slice []AddrRange) AddrRangeSeq { 70 var limit int64 71 for _, ar := range slice { 72 len64 := int64(ar.Length()) 73 if len64 < 0 { 74 panic(fmt.Sprintf("Length of AddrRange %v overflows int64", ar)) 75 } 76 sum := limit + len64 77 if sum < limit { 78 panic(fmt.Sprintf("Total length of AddrRanges %v overflows int64", slice)) 79 } 80 limit = sum 81 } 82 return addrRangeSeqFromSliceLimited(slice, limit) 83 } 84 85 // Preconditions: 86 // - The combined length of all AddrRanges in slice <= limit. 87 // - limit >= 0. 88 // - If len(slice) != 0, then limit > 0. 89 func addrRangeSeqFromSliceLimited(slice []AddrRange, limit int64) AddrRangeSeq { 90 switch len(slice) { 91 case 0: 92 return AddrRangeSeq{} 93 case 1: 94 return AddrRangeSeq{ 95 length: 1, 96 offset: slice[0].Start, 97 limit: Addr(limit), 98 } 99 default: 100 return AddrRangeSeq{ 101 data: unsafe.Pointer(&slice[0]), 102 length: len(slice), 103 limit: Addr(limit), 104 } 105 } 106 } 107 108 // IsEmpty returns true if ars.NumRanges() == 0. 109 // 110 // Note that since AddrRangeSeq may contain AddrRanges with a length of zero, 111 // an AddrRange representing 0 bytes (AddrRangeSeq.NumBytes() == 0) is not 112 // necessarily empty. 113 func (ars AddrRangeSeq) IsEmpty() bool { 114 return ars.length == 0 115 } 116 117 // NumRanges returns the number of AddrRanges in ars. 118 func (ars AddrRangeSeq) NumRanges() int { 119 return ars.length 120 } 121 122 // NumBytes returns the number of bytes represented by ars. 123 func (ars AddrRangeSeq) NumBytes() int64 { 124 return int64(ars.limit) 125 } 126 127 // Head returns the first AddrRange in ars. 128 // 129 // Preconditions: !ars.IsEmpty(). 130 func (ars AddrRangeSeq) Head() AddrRange { 131 if ars.length == 0 { 132 panic("empty AddrRangeSeq") 133 } 134 if ars.length == 1 { 135 return AddrRange{ars.offset, ars.offset + ars.limit} 136 } 137 ar := *(*AddrRange)(ars.data) 138 ar.Start += ars.offset 139 if ar.Length() > ars.limit { 140 ar.End = ar.Start + ars.limit 141 } 142 return ar 143 } 144 145 // Tail returns an AddrRangeSeq consisting of all AddrRanges in ars after the 146 // first. 147 // 148 // Preconditions: !ars.IsEmpty(). 149 func (ars AddrRangeSeq) Tail() AddrRangeSeq { 150 if ars.length == 0 { 151 panic("empty AddrRangeSeq") 152 } 153 if ars.length == 1 { 154 return AddrRangeSeq{} 155 } 156 return ars.externalTail() 157 } 158 159 // Preconditions: ars.length >= 2. 160 func (ars AddrRangeSeq) externalTail() AddrRangeSeq { 161 data := (*AddrRange)(ars.data) 162 headLen := data.Length() - ars.offset 163 var tailLimit int64 164 if ars.limit > headLen { 165 tailLimit = int64(ars.limit - headLen) 166 } 167 extSlice := gohacks.Slice(data, ars.length) 168 return addrRangeSeqFromSliceLimited(extSlice[1:], tailLimit) 169 } 170 171 // DropFirst returns an AddrRangeSeq equivalent to ars, but with the first n 172 // bytes omitted. If n > ars.NumBytes(), DropFirst returns an empty 173 // AddrRangeSeq. 174 // 175 // If !ars.IsEmpty() and ars.Head().Length() == 0, DropFirst will always omit 176 // at least ars.Head(), even if n == 0. This guarantees that the basic pattern 177 // of: 178 // 179 // for !ars.IsEmpty() { 180 // n, err = doIOWith(ars.Head()) 181 // if err != nil { 182 // return err 183 // } 184 // ars = ars.DropFirst(n) 185 // } 186 // 187 // works even in the presence of zero-length AddrRanges. 188 // 189 // Preconditions: n >= 0. 190 func (ars AddrRangeSeq) DropFirst(n int) AddrRangeSeq { 191 if n < 0 { 192 panic(fmt.Sprintf("invalid n: %d", n)) 193 } 194 return ars.DropFirst64(int64(n)) 195 } 196 197 // DropFirst64 is equivalent to DropFirst but takes an int64. 198 func (ars AddrRangeSeq) DropFirst64(n int64) AddrRangeSeq { 199 if n < 0 { 200 panic(fmt.Sprintf("invalid n: %d", n)) 201 } 202 if Addr(n) > ars.limit { 203 return AddrRangeSeq{} 204 } 205 // Handle initial empty AddrRange. 206 switch ars.length { 207 case 0: 208 return AddrRangeSeq{} 209 case 1: 210 if ars.limit == 0 { 211 return AddrRangeSeq{} 212 } 213 default: 214 if rawHeadLen := (*AddrRange)(ars.data).Length(); ars.offset == rawHeadLen { 215 ars = ars.externalTail() 216 } 217 } 218 for n != 0 { 219 // Calling ars.Head() here is surprisingly expensive, so inline getting 220 // the head's length. 221 var headLen Addr 222 if ars.length == 1 { 223 headLen = ars.limit 224 } else { 225 headLen = (*AddrRange)(ars.data).Length() - ars.offset 226 } 227 if Addr(n) < headLen { 228 // Dropping ends partway through the head AddrRange. 229 ars.offset += Addr(n) 230 ars.limit -= Addr(n) 231 return ars 232 } 233 n -= int64(headLen) 234 ars = ars.Tail() 235 } 236 return ars 237 } 238 239 // TakeFirst returns an AddrRangeSeq equivalent to ars, but iterating at most n 240 // bytes. TakeFirst never removes AddrRanges from ars; AddrRanges beyond the 241 // first n bytes are reduced to a length of zero, but will still be iterated. 242 // 243 // Preconditions: n >= 0. 244 func (ars AddrRangeSeq) TakeFirst(n int) AddrRangeSeq { 245 if n < 0 { 246 panic(fmt.Sprintf("invalid n: %d", n)) 247 } 248 return ars.TakeFirst64(int64(n)) 249 } 250 251 // TakeFirst64 is equivalent to TakeFirst but takes an int64. 252 func (ars AddrRangeSeq) TakeFirst64(n int64) AddrRangeSeq { 253 if n < 0 { 254 panic(fmt.Sprintf("invalid n: %d", n)) 255 } 256 if ars.limit > Addr(n) { 257 ars.limit = Addr(n) 258 } 259 return ars 260 } 261 262 // String implements fmt.Stringer.String. 263 func (ars AddrRangeSeq) String() string { 264 // This is deliberately chosen to be the same as fmt's automatic stringer 265 // for []AddrRange. 266 var buf bytes.Buffer 267 buf.WriteByte('[') 268 var sep string 269 for !ars.IsEmpty() { 270 buf.WriteString(sep) 271 sep = " " 272 buf.WriteString(ars.Head().String()) 273 ars = ars.Tail() 274 } 275 buf.WriteByte(']') 276 return buf.String() 277 }