diff --git a/go.mod b/go.mod index 6308bc259ca..44d8c461dd1 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/golang/mock v1.3.1 github.com/golang/protobuf v1.3.2 github.com/golang/snappy v0.0.0-20170215233205-553a64147049 + github.com/google/btree v1.0.0 // indirect github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf // indirect github.com/gorilla/websocket v0.0.0-20160912153041-2d1e4548da23 github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 @@ -49,6 +50,8 @@ require ( github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 github.com/opentracing/opentracing-go v1.1.0 diff --git a/go/mysql/encoding.go b/go/mysql/encoding.go index c9333522cdf..53b9e2efc73 100644 --- a/go/mysql/encoding.go +++ b/go/mysql/encoding.go @@ -281,3 +281,17 @@ func readLenEncStringAsBytes(data []byte, pos int) ([]byte, int, bool) { } return data[pos : pos+s], pos + s, true } + +func readLenEncStringAsBytesCopy(data []byte, pos int) ([]byte, int, bool) { + size, pos, ok := readLenEncInt(data, pos) + if !ok { + return nil, 0, false + } + s := int(size) + if pos+s-1 >= len(data) { + return nil, 0, false + } + result := make([]byte, size) + copy(result, data[pos:pos+s]) + return result, pos + s, true +} diff --git a/go/mysql/encoding_test.go b/go/mysql/encoding_test.go index 2ca9a84c934..ac38b5c7db0 100644 --- a/go/mysql/encoding_test.go +++ b/go/mysql/encoding_test.go @@ -303,6 +303,24 @@ func TestEncString(t *testing.T) { t.Errorf("readLenEncStringAsBytes returned ok=true for empty value %v", test.value) } + // Check successful decoding as bytes. + gotbcopy, posCopy, ok := readLenEncStringAsBytesCopy(test.lenEncoded, 0) + if !ok || string(gotb) != test.value || pos != len(test.lenEncoded) { + t.Errorf("readLenEncString returned %v/%v/%v but expected %v/%v/%v", gotbcopy, posCopy, ok, test.value, len(test.lenEncoded), true) + } + + // Check failed decoding as bytes with shorter data. + _, _, ok = readLenEncStringAsBytesCopy(test.lenEncoded[:len(test.lenEncoded)-1], 0) + if ok { + t.Errorf("readLenEncStringAsBytes returned ok=true for shorter value %v", test.value) + } + + // Check failed decoding as bytes with no data. + _, _, ok = readLenEncStringAsBytesCopy([]byte{}, 0) + if ok { + t.Errorf("readLenEncStringAsBytes returned ok=true for empty value %v", test.value) + } + // null encoded tests. // Check successful encoding. diff --git a/go/mysql/query.go b/go/mysql/query.go index 6ff9d1e9bc0..f6435bf4b64 100644 --- a/go/mysql/query.go +++ b/go/mysql/query.go @@ -261,7 +261,7 @@ func (c *Conn) parseRow(data []byte, fields []*querypb.Field) ([]sqltypes.Value, } var s []byte var ok bool - s, pos, ok = readLenEncStringAsBytes(data, pos) + s, pos, ok = readLenEncStringAsBytesCopy(data, pos) if !ok { return nil, NewSQLError(CRMalformedPacket, SSUnknownSQLState, "decoding string failed") } @@ -823,7 +823,7 @@ func (c *Conn) parseStmtArgs(data []byte, typ querypb.Type, pos int) (sqltypes.V } case sqltypes.Decimal, sqltypes.Text, sqltypes.Blob, sqltypes.VarChar, sqltypes.VarBinary, sqltypes.Char, sqltypes.Bit, sqltypes.Enum, sqltypes.Set, sqltypes.Geometry, sqltypes.Binary, sqltypes.TypeJSON: - val, pos, ok := readLenEncStringAsBytes(data, pos) + val, pos, ok := readLenEncStringAsBytesCopy(data, pos) return sqltypes.MakeTrusted(sqltypes.VarBinary, val), pos, ok default: return sqltypes.NULL, pos, false