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 }