Skip to content

Commit ac32f52

Browse files
committed
rlp: fix encReader returning nil buffers to the pool
The bug can cause crashes if Read is called after EOF has been returned. No code performs such calls right now, but hitting the bug gets more likely as rlp.EncodeToReader gets used in more places.
1 parent e2d7c1a commit ac32f52

File tree

2 files changed

+32
-4
lines changed

2 files changed

+32
-4
lines changed

rlp/encode.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ func Encode(w io.Writer, val interface{}) error {
9090
return outer.encode(val)
9191
}
9292
eb := encbufPool.Get().(*encbuf)
93-
eb.reset()
9493
defer encbufPool.Put(eb)
94+
eb.reset()
9595
if err := eb.encode(val); err != nil {
9696
return err
9797
}
@@ -102,8 +102,8 @@ func Encode(w io.Writer, val interface{}) error {
102102
// Please see the documentation of Encode for the encoding rules.
103103
func EncodeToBytes(val interface{}) ([]byte, error) {
104104
eb := encbufPool.Get().(*encbuf)
105-
eb.reset()
106105
defer encbufPool.Put(eb)
106+
eb.reset()
107107
if err := eb.encode(val); err != nil {
108108
return nil, err
109109
}
@@ -288,8 +288,13 @@ type encReader struct {
288288
func (r *encReader) Read(b []byte) (n int, err error) {
289289
for {
290290
if r.piece = r.next(); r.piece == nil {
291-
encbufPool.Put(r.buf)
292-
r.buf = nil
291+
// Put the encode buffer back into the pool at EOF when it
292+
// is first encountered. Subsequent calls still return EOF
293+
// as the error but the buffer is no longer valid.
294+
if r.buf != nil {
295+
encbufPool.Put(r.buf)
296+
r.buf = nil
297+
}
293298
return n, io.EOF
294299
}
295300
nn := copy(b[n:], r.piece)

rlp/encode_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"io"
2424
"io/ioutil"
2525
"math/big"
26+
"sync"
2627
"testing"
2728
)
2829

@@ -306,3 +307,25 @@ func TestEncodeToReaderPiecewise(t *testing.T) {
306307
return output, nil
307308
})
308309
}
310+
311+
// This is a regression test verifying that encReader
312+
// returns its encbuf to the pool only once.
313+
func TestEncodeToReaderReturnToPool(t *testing.T) {
314+
buf := make([]byte, 50)
315+
wg := new(sync.WaitGroup)
316+
for i := 0; i < 5; i++ {
317+
wg.Add(1)
318+
go func() {
319+
for i := 0; i < 1000; i++ {
320+
_, r, _ := EncodeToReader("foo")
321+
ioutil.ReadAll(r)
322+
r.Read(buf)
323+
r.Read(buf)
324+
r.Read(buf)
325+
r.Read(buf)
326+
}
327+
wg.Done()
328+
}()
329+
}
330+
wg.Wait()
331+
}

0 commit comments

Comments
 (0)