github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/safemem/block_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 "fmt" 19 "unsafe" 20 21 "github.com/SagerNet/gvisor/pkg/gohacks" 22 "github.com/SagerNet/gvisor/pkg/safecopy" 23 "github.com/SagerNet/gvisor/pkg/sync" 24 ) 25 26 // A Block is a range of contiguous bytes, similar to []byte but with the 27 // following differences: 28 // 29 // - The memory represented by a Block may require the use of safecopy to 30 // access. 31 // 32 // - Block does not carry a capacity and cannot be expanded. 33 // 34 // Blocks are immutable and may be copied by value. The zero value of Block 35 // represents an empty range, analogous to a nil []byte. 36 type Block struct { 37 // [start, start+length) is the represented memory. 38 // 39 // start is an unsafe.Pointer to ensure that Block prevents the represented 40 // memory from being garbage-collected. 41 start unsafe.Pointer 42 length int 43 44 // needSafecopy is true if accessing the represented memory requires the 45 // use of safecopy. 46 needSafecopy bool 47 } 48 49 // BlockFromSafeSlice returns a Block equivalent to slice, which is safe to 50 // access without safecopy. 51 func BlockFromSafeSlice(slice []byte) Block { 52 return blockFromSlice(slice, false) 53 } 54 55 // BlockFromUnsafeSlice returns a Block equivalent to bs, which is not safe to 56 // access without safecopy. 57 func BlockFromUnsafeSlice(slice []byte) Block { 58 return blockFromSlice(slice, true) 59 } 60 61 func blockFromSlice(slice []byte, needSafecopy bool) Block { 62 if len(slice) == 0 { 63 return Block{} 64 } 65 return Block{ 66 start: unsafe.Pointer(&slice[0]), 67 length: len(slice), 68 needSafecopy: needSafecopy, 69 } 70 } 71 72 // BlockFromSafePointer returns a Block equivalent to [ptr, ptr+length), which is 73 // safe to access without safecopy. 74 // 75 // Preconditions: ptr+length does not overflow. 76 func BlockFromSafePointer(ptr unsafe.Pointer, length int) Block { 77 return blockFromPointer(ptr, length, false) 78 } 79 80 // BlockFromUnsafePointer returns a Block equivalent to [ptr, ptr+len), which 81 // is not safe to access without safecopy. 82 // 83 // Preconditions: ptr+len does not overflow. 84 func BlockFromUnsafePointer(ptr unsafe.Pointer, length int) Block { 85 return blockFromPointer(ptr, length, true) 86 } 87 88 func blockFromPointer(ptr unsafe.Pointer, length int, needSafecopy bool) Block { 89 if uptr := uintptr(ptr); uptr+uintptr(length) < uptr { 90 panic(fmt.Sprintf("ptr %#x + len %#x overflows", uptr, length)) 91 } 92 return Block{ 93 start: ptr, 94 length: length, 95 needSafecopy: needSafecopy, 96 } 97 } 98 99 // DropFirst returns a Block equivalent to b, but with the first n bytes 100 // omitted. It is analogous to the [n:] operation on a slice, except that if n 101 // > b.Len(), DropFirst returns an empty Block instead of panicking. 102 // 103 // Preconditions: n >= 0. 104 func (b Block) DropFirst(n int) Block { 105 if n < 0 { 106 panic(fmt.Sprintf("invalid n: %d", n)) 107 } 108 return b.DropFirst64(uint64(n)) 109 } 110 111 // DropFirst64 is equivalent to DropFirst but takes a uint64. 112 func (b Block) DropFirst64(n uint64) Block { 113 if n >= uint64(b.length) { 114 return Block{} 115 } 116 return Block{ 117 start: unsafe.Pointer(uintptr(b.start) + uintptr(n)), 118 length: b.length - int(n), 119 needSafecopy: b.needSafecopy, 120 } 121 } 122 123 // TakeFirst returns a Block equivalent to the first n bytes of b. It is 124 // analogous to the [:n] operation on a slice, except that if n > b.Len(), 125 // TakeFirst returns a copy of b instead of panicking. 126 // 127 // Preconditions: n >= 0. 128 func (b Block) TakeFirst(n int) Block { 129 if n < 0 { 130 panic(fmt.Sprintf("invalid n: %d", n)) 131 } 132 return b.TakeFirst64(uint64(n)) 133 } 134 135 // TakeFirst64 is equivalent to TakeFirst but takes a uint64. 136 func (b Block) TakeFirst64(n uint64) Block { 137 if n == 0 { 138 return Block{} 139 } 140 if n >= uint64(b.length) { 141 return b 142 } 143 return Block{ 144 start: b.start, 145 length: int(n), 146 needSafecopy: b.needSafecopy, 147 } 148 } 149 150 // ToSlice returns a []byte equivalent to b. 151 func (b Block) ToSlice() []byte { 152 return *(*[]byte)(unsafe.Pointer(&gohacks.SliceHeader{ 153 Data: b.start, 154 Len: b.length, 155 Cap: b.length, 156 })) 157 } 158 159 // Addr returns b's start address as a uintptr. It returns uintptr instead of 160 // unsafe.Pointer so that code using safemem cannot obtain unsafe.Pointers 161 // without importing the unsafe package explicitly. 162 // 163 // Note that a uintptr is not recognized as a pointer by the garbage collector, 164 // such that if there are no uses of b after a call to b.Addr() and the address 165 // is to Go-managed memory, the returned uintptr does not prevent garbage 166 // collection of the pointee. 167 func (b Block) Addr() uintptr { 168 return uintptr(b.start) 169 } 170 171 // Len returns b's length in bytes. 172 func (b Block) Len() int { 173 return b.length 174 } 175 176 // NeedSafecopy returns true if accessing b.ToSlice() requires the use of safecopy. 177 func (b Block) NeedSafecopy() bool { 178 return b.needSafecopy 179 } 180 181 // String implements fmt.Stringer.String. 182 func (b Block) String() string { 183 if uintptr(b.start) == 0 && b.length == 0 { 184 return "<nil>" 185 } 186 var suffix string 187 if b.needSafecopy { 188 suffix = "*" 189 } 190 return fmt.Sprintf("[%#x-%#x)%s", uintptr(b.start), uintptr(b.start)+uintptr(b.length), suffix) 191 } 192 193 // Copy copies src.Len() or dst.Len() bytes, whichever is less, from src 194 // to dst and returns the number of bytes copied. 195 // 196 // If src and dst overlap, the data stored in dst is unspecified. 197 func Copy(dst, src Block) (int, error) { 198 if !dst.needSafecopy && !src.needSafecopy { 199 return copy(dst.ToSlice(), src.ToSlice()), nil 200 } 201 202 n := dst.length 203 if n > src.length { 204 n = src.length 205 } 206 if n == 0 { 207 return 0, nil 208 } 209 210 switch { 211 case dst.needSafecopy && !src.needSafecopy: 212 return safecopy.CopyOut(dst.start, src.TakeFirst(n).ToSlice()) 213 case !dst.needSafecopy && src.needSafecopy: 214 return safecopy.CopyIn(dst.TakeFirst(n).ToSlice(), src.start) 215 case dst.needSafecopy && src.needSafecopy: 216 n64, err := safecopy.Copy(dst.start, src.start, uintptr(n)) 217 return int(n64), err 218 default: 219 panic("unreachable") 220 } 221 } 222 223 // Zero sets all bytes in dst to 0 and returns the number of bytes zeroed. 224 func Zero(dst Block) (int, error) { 225 if !dst.needSafecopy { 226 bs := dst.ToSlice() 227 if !sync.RaceEnabled { 228 // If the race detector isn't enabled, the golang 229 // compiler replaces the next loop with memclr 230 // (https://github.com/golang/go/issues/5373). 231 for i := range bs { 232 bs[i] = 0 233 } 234 } else { 235 bsLen := len(bs) 236 if bsLen == 0 { 237 return 0, nil 238 } 239 bs[0] = 0 240 for i := 1; i < bsLen; i *= 2 { 241 copy(bs[i:], bs[:i]) 242 } 243 } 244 return len(bs), nil 245 } 246 247 n64, err := safecopy.ZeroOut(dst.start, uintptr(dst.length)) 248 return int(n64), err 249 } 250 251 // Safecopy atomics are no slower than non-safecopy atomics, so use the former 252 // even when !b.needSafecopy to get consistent alignment checking. 253 254 // SwapUint32 invokes safecopy.SwapUint32 on the first 4 bytes of b. 255 // 256 // Preconditions: b.Len() >= 4. 257 func SwapUint32(b Block, new uint32) (uint32, error) { 258 if b.length < 4 { 259 panic(fmt.Sprintf("insufficient length: %d", b.length)) 260 } 261 return safecopy.SwapUint32(b.start, new) 262 } 263 264 // SwapUint64 invokes safecopy.SwapUint64 on the first 8 bytes of b. 265 // 266 // Preconditions: b.Len() >= 8. 267 func SwapUint64(b Block, new uint64) (uint64, error) { 268 if b.length < 8 { 269 panic(fmt.Sprintf("insufficient length: %d", b.length)) 270 } 271 return safecopy.SwapUint64(b.start, new) 272 } 273 274 // CompareAndSwapUint32 invokes safecopy.CompareAndSwapUint32 on the first 4 275 // bytes of b. 276 // 277 // Preconditions: b.Len() >= 4. 278 func CompareAndSwapUint32(b Block, old, new uint32) (uint32, error) { 279 if b.length < 4 { 280 panic(fmt.Sprintf("insufficient length: %d", b.length)) 281 } 282 return safecopy.CompareAndSwapUint32(b.start, old, new) 283 } 284 285 // LoadUint32 invokes safecopy.LoadUint32 on the first 4 bytes of b. 286 // 287 // Preconditions: b.Len() >= 4. 288 func LoadUint32(b Block) (uint32, error) { 289 if b.length < 4 { 290 panic(fmt.Sprintf("insufficient length: %d", b.length)) 291 } 292 return safecopy.LoadUint32(b.start) 293 }