diff --git a/lib/protocol/protocol.go b/lib/protocol/protocol.go index 59991732a..7ffae79a8 100644 --- a/lib/protocol/protocol.go +++ b/lib/protocol/protocol.go @@ -959,14 +959,17 @@ func (c *rawConnection) Statistics() Statistics { func (c *rawConnection) lz4Compress(src []byte) ([]byte, error) { var err error - buf := BufferPool.Get(len(src)) - buf, err = lz4.Encode(buf, src) + buf := BufferPool.Get(lz4.CompressBound(len(src))) + compressed, err := lz4.Encode(buf, src) if err != nil { return nil, err } + if &compressed[0] != &buf[0] { + panic("bug: lz4.Compress allocated, which it must not (should use buffer pool)") + } - binary.BigEndian.PutUint32(buf, binary.LittleEndian.Uint32(buf)) - return buf, nil + binary.BigEndian.PutUint32(compressed, binary.LittleEndian.Uint32(compressed)) + return compressed, nil } func (c *rawConnection) lz4Decompress(src []byte) ([]byte, error) { @@ -974,9 +977,12 @@ func (c *rawConnection) lz4Decompress(src []byte) ([]byte, error) { binary.LittleEndian.PutUint32(src, size) var err error buf := BufferPool.Get(int(size)) - buf, err = lz4.Decode(buf, src) + decoded, err := lz4.Decode(buf, src) if err != nil { return nil, err } - return buf, nil + if &decoded[0] != &buf[0] { + panic("bug: lz4.Decode allocated, which it must not (should use buffer pool)") + } + return decoded, nil } diff --git a/lib/protocol/protocol_test.go b/lib/protocol/protocol_test.go index 6075d719d..4f973c619 100644 --- a/lib/protocol/protocol_test.go +++ b/lib/protocol/protocol_test.go @@ -434,6 +434,38 @@ func TestLZ4Compression(t *testing.T) { } } +func TestStressLZ4CompressGrows(t *testing.T) { + c := new(rawConnection) + success := 0 + for i := 0; i < 100; i++ { + // Create a slize that is precisely one min block size, fill it with + // random data. This shouldn't compress at all, so will in fact + // become larger when LZ4 does its thing. + data := make([]byte, MinBlockSize) + if _, err := rand.Reader.Read(data); err != nil { + t.Fatal("randomness failure") + } + + comp, err := c.lz4Compress(data) + if err != nil { + t.Fatal("unexpected compression error: ", err) + } + if len(comp) < len(data) { + // data size should grow. We must have been really unlucky in + // the random generation, try again. + continue + } + + // Putting it into the buffer pool shouldn't panic because the block + // should come from there to begin with. + BufferPool.Put(comp) + success++ + } + if success == 0 { + t.Fatal("unable to find data that grows when compressed") + } +} + func TestCheckFilename(t *testing.T) { cases := []struct { name string