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  }