go.uber.org/yarpc@v1.72.1/internal/bufferpool/buffer.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package bufferpool
    22  
    23  import (
    24  	"bytes"
    25  	"io"
    26  )
    27  
    28  // Buffer represents a poolable buffer. It wraps an underlying
    29  // *bytes.Buffer with lightweight detection of races.
    30  type Buffer struct {
    31  	pool *Pool
    32  
    33  	// version is an ever-incrementing integer on every operation.
    34  	// it ensures that we don't perform multiple overlapping operations.
    35  	version uint
    36  
    37  	// released tracks whether the buffer has been released.
    38  	released bool
    39  
    40  	buf *bytes.Buffer
    41  }
    42  
    43  func newBuffer(pool *Pool) *Buffer {
    44  	return &Buffer{
    45  		pool: pool,
    46  		buf:  &bytes.Buffer{},
    47  	}
    48  }
    49  
    50  func (b *Buffer) checkUseAfterFree() {
    51  	if b.released || b.buf == nil {
    52  		panic("use-after-free of pooled buffer")
    53  	}
    54  }
    55  
    56  func (b *Buffer) preOp() uint {
    57  	b.checkUseAfterFree()
    58  	b.version++
    59  	return b.version
    60  }
    61  
    62  func (b *Buffer) postOp(v uint) {
    63  	b.checkUseAfterFree()
    64  	if v != b.version || b.released {
    65  		panic("concurrent use of pooled buffer")
    66  	}
    67  	b.version++
    68  }
    69  
    70  // Read is the same as bytes.Buffer.Read.
    71  func (b *Buffer) Read(p []byte) (int, error) {
    72  	version := b.preOp()
    73  	n, err := b.buf.Read(p)
    74  	b.postOp(version)
    75  	return n, err
    76  }
    77  
    78  // ReadFrom is the same as bytes.Buffer.ReadFrom.
    79  func (b *Buffer) ReadFrom(r io.Reader) (int64, error) {
    80  	version := b.preOp()
    81  	n, err := b.buf.ReadFrom(r)
    82  	b.postOp(version)
    83  	return n, err
    84  }
    85  
    86  // Write is the same as bytes.Buffer.Write.
    87  func (b *Buffer) Write(p []byte) (int, error) {
    88  	version := b.preOp()
    89  	n, err := b.buf.Write(p)
    90  	b.postOp(version)
    91  	return n, err
    92  }
    93  
    94  // WriteTo is the same as bytes.Buffer.WriteTo.
    95  func (b *Buffer) WriteTo(w io.Writer) (int64, error) {
    96  	version := b.preOp()
    97  	n, err := b.buf.WriteTo(w)
    98  	b.postOp(version)
    99  	return n, err
   100  }
   101  
   102  // Bytes returns the bytes in the underlying buffer, as well as a
   103  // function to call when the caller is done using the bytes.
   104  // This is easy to mis-use and lead to a use-after-free that
   105  // cannot be detected, so it is strongly recommended that this method
   106  // is NOT used.
   107  func (b *Buffer) Bytes() []byte {
   108  	return b.buf.Bytes()
   109  }
   110  
   111  // Len is the same as bytes.Buffer.Len.
   112  func (b *Buffer) Len() int {
   113  	version := b.preOp()
   114  	n := b.buf.Len()
   115  	b.postOp(version)
   116  	return n
   117  }
   118  
   119  // Reset is the same as bytes.Buffer.Reset.
   120  func (b *Buffer) Reset() {
   121  	version := b.preOp()
   122  	b.buf.Reset()
   123  	b.postOp(version)
   124  }
   125  
   126  // Release releases the buffer back to the buffer pool.
   127  func (b *Buffer) Release() {
   128  	// Increment the version so overlapping operations fail.
   129  	b.postOp(b.preOp())
   130  
   131  	if b.pool.testDetectUseAfterFree {
   132  		b.releaseDetectUseAfterFree()
   133  		return
   134  	}
   135  
   136  	// Before releasing a buffer, we should reset it to "clear" the buffer
   137  	// while holding on to the capacity of the buffer.
   138  	b.Reset()
   139  
   140  	// We must mark released after the `Reset`, so that `Reset` doesn't
   141  	// trigger use-after-free.
   142  	b.released = true
   143  
   144  	b.pool.release(b)
   145  }
   146  
   147  func (b *Buffer) reuse() {
   148  	b.released = false
   149  }
   150  
   151  func (b *Buffer) releaseDetectUseAfterFree() {
   152  	// Detect any lingering reads of the underlying data by resetting the data.
   153  	// We repeat it in a goroutine to trigger the race detector.
   154  	overwriteData(b.Bytes())
   155  	go overwriteData(b.Bytes())
   156  
   157  	// This will cause any future accesses to panic.
   158  	b.released = true
   159  	b.buf = nil
   160  }
   161  
   162  func overwriteData(bs []byte) {
   163  	for i := range bs {
   164  		bs[i] = byte(i)
   165  	}
   166  }
   167  
   168  // AutoReleaseBuffer wraps a Buffer in a io.ReadCloser implementation
   169  // that returns the underlying Buffer to the pool on Close().
   170  type AutoReleaseBuffer struct {
   171  	*Buffer
   172  }
   173  
   174  // NewAutoReleaseBuffer creates a AutoReleaseBuffer
   175  func NewAutoReleaseBuffer() AutoReleaseBuffer {
   176  	buf := Get()
   177  	return AutoReleaseBuffer{
   178  		Buffer: buf,
   179  	}
   180  }
   181  
   182  func (arb AutoReleaseBuffer) Close() error {
   183  	Put(arb.Buffer)
   184  	return nil
   185  }