Skip to content

Commit 34c5cb1

Browse files
Peter Wilhelmsson2hdddg
authored andcommitted
Error while expanding buffer in dechunker
Serious issue that could cause corrupt data in records. Fixes #183
1 parent e2f6706 commit 34c5cb1

File tree

8 files changed

+41
-19
lines changed

8 files changed

+41
-19
lines changed

neo4j/internal/bolt/bolt3server_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func (s *bolt3server) waitForHello() {
9191
}
9292

9393
func (s *bolt3server) receiveMsg() *testStruct {
94-
buf, err := dechunkMessage(s.conn, []byte{})
94+
_, buf, err := dechunkMessage(s.conn, []byte{})
9595
if err != nil {
9696
panic(err)
9797
}

neo4j/internal/bolt/bolt4server_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func (s *bolt4server) waitForHello() map[string]interface{} {
9494
}
9595

9696
func (s *bolt4server) receiveMsg() *testStruct {
97-
buf, err := dechunkMessage(s.conn, []byte{})
97+
_, buf, err := dechunkMessage(s.conn, []byte{})
9898
if err != nil {
9999
panic(err)
100100
}

neo4j/internal/bolt/chunker_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func TestChunker(ot *testing.T) {
8181

8282
receiveAndAssertMessage := func(t *testing.T, rd io.Reader, expected []byte) {
8383
t.Helper()
84-
msg, err := dechunkMessage(rd, []byte{})
84+
_, msg, err := dechunkMessage(rd, []byte{})
8585
AssertNoError(t, err)
8686
assertSlices(t, msg, expected)
8787
}

neo4j/internal/bolt/dechunker.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,37 @@ import (
2424
"io"
2525
)
2626

27-
func dechunkMessage(rd io.Reader, msgBuf []byte) ([]byte, error) {
27+
// dechunkMessage takes a buffer to be reused and returns the reusable buffer
28+
// (might have been reallocated to handle growth), the message buffer and
29+
// error.
30+
func dechunkMessage(rd io.Reader, msgBuf []byte) ([]byte, []byte, error) {
2831
sizeBuf := []byte{0x00, 0x00}
2932
off := 0
3033

3134
for {
3235
_, err := io.ReadFull(rd, sizeBuf)
3336
if err != nil {
34-
return msgBuf, err
37+
return msgBuf, nil, err
3538
}
3639
chunkSize := int(binary.BigEndian.Uint16(sizeBuf))
3740
if chunkSize == 0 {
3841
if off > 0 {
39-
return msgBuf[:off], nil
42+
return msgBuf, msgBuf[:off], nil
4043
}
4144
// Got a nop chunk
4245
continue
4346
}
4447

4548
// Need to expand buffer
4649
if (off + chunkSize) > cap(msgBuf) {
47-
msgBuf = append(msgBuf, make([]byte, off+chunkSize)...)
50+
newMsgBuf := make([]byte, (off+chunkSize)+4096)
51+
copy(newMsgBuf, msgBuf)
52+
msgBuf = newMsgBuf
4853
}
4954
// Read the chunk into buffer
5055
_, err = io.ReadFull(rd, msgBuf[off:(off+chunkSize)])
5156
if err != nil {
52-
return msgBuf, err
57+
return msgBuf, nil, err
5358
}
5459
off += chunkSize
5560
}

neo4j/internal/bolt/dechunker_test.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,16 @@ func TestDechunker(t *testing.T) {
3939
{size: 1021, max: 0x7},
4040
{size: 0xffff78, max: 0x30},
4141
{size: 3, max: 0xffff},
42+
{size: 1021, max: 0xff90},
43+
{size: 0xffff78, max: 0xff90},
4244
}
43-
for _, msg := range messages {
45+
for msgi, msg := range messages {
4446
// Prepare message
4547
str := &bytes.Buffer{}
4648
total := msg.size
4749
this := uint16(0)
4850

51+
b := byte(0)
4952
for total > 0 {
5053
// Write size
5154
if total > uint32(msg.max) {
@@ -60,15 +63,30 @@ func TestDechunker(t *testing.T) {
6063

6164
// Write data
6265
buf = make([]byte, int(this))
66+
for i := range buf {
67+
buf[i] = b
68+
b++
69+
}
6370
str.Write(buf)
6471
}
6572
// Write end of mesage marker
6673
str.Write([]byte{0x00, 0x00})
6774

6875
// Dechunk the message
69-
buf, err = dechunkMessage(str, buf)
76+
var msgBuf []byte
77+
buf, msgBuf, err = dechunkMessage(str, buf)
7078
AssertNoError(t, err)
71-
AssertLen(t, buf, int(msg.size))
79+
AssertLen(t, msgBuf, int(msg.size))
80+
// Check content of buffer
81+
b = 0
82+
for i := range msgBuf {
83+
if msgBuf[i] != b {
84+
t.Errorf("Wrong content in buffer at %d, %d vs %d (message %d)", i, msgBuf[i], b, msgi)
85+
return
86+
}
87+
b++
88+
}
89+
7290
// Check that buffer increases or stays put
7391
if cap(buf) < prevCap {
7492
t.Errorf("Underlying buffer should be reused")

neo4j/internal/bolt/hydratedehydrate_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func TestDehydrateHydrate(ot *testing.T) {
4747
buf := &bytes.Buffer{}
4848
out.send(buf)
4949

50-
byts, err := dechunkMessage(buf, []byte{})
50+
_, byts, err := dechunkMessage(buf, []byte{})
5151
if err != nil {
5252
t.Fatal(err)
5353
}

neo4j/internal/bolt/incoming.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,17 @@ import (
2323
)
2424

2525
type incoming struct {
26-
buf []byte
26+
buf []byte // Reused buffer
2727
hyd hydrator
2828
}
2929

3030
func (i *incoming) next(rd io.Reader) (interface{}, error) {
3131
// Get next message from transport layer
32-
buf, err := dechunkMessage(rd, i.buf)
32+
var err error
33+
var msg []byte
34+
i.buf, msg, err = dechunkMessage(rd, i.buf)
3335
if err != nil {
3436
return nil, err
3537
}
36-
// Reuse buffer for next dechunk
37-
i.buf = buf
38-
39-
return i.hyd.hydrate(buf)
38+
return i.hyd.hydrate(msg)
4039
}

neo4j/internal/bolt/outgoing_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func TestOutgoing(ot *testing.T) {
9999
out.send(buf)
100100

101101
// Dechunk it
102-
byts, err := dechunkMessage(buf, []byte{})
102+
_, byts, err := dechunkMessage(buf, []byte{})
103103
if err != nil {
104104
t.Fatal(err)
105105
}

0 commit comments

Comments
 (0)