Fix decoding of message headers for messages with lengths >= 2^24.

recvMsg was interpreting buf[1] as the payload format instead of buf[0];
since compressionNone (the only thing supported) == 0, recvMsg got lucky
for message lengths under 2^24, which has buf[1] == 0.

Fix the error, and ditch the constants in recvMsg. I think they were the
cause of the bug.

Also make encode fail more clearly if someone tries to transmit a
message that exceeds 2^32 bytes.

Fixes #399.
This commit is contained in:
David Symonds
2015-10-15 18:43:49 +11:00
parent f7d1653e30
commit d3cb35a50a
2 changed files with 18 additions and 17 deletions

View File

@ -38,6 +38,7 @@ import (
"encoding/binary"
"fmt"
"io"
"math"
"math/rand"
"os"
"time"
@ -139,42 +140,36 @@ type msgFixedHeader struct {
// EOF is returned with nil msg and 0 pf if the entire stream is done. Other
// non-nil error is returned if something is wrong on reading.
func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
const (
headerSize = 5
formatIndex = 1
)
var hdr msgFixedHeader
var buf [headerSize]byte
var buf [5]byte // see msgFixedHeader
if _, err := io.ReadFull(p.s, buf[:]); err != nil {
return 0, nil, err
}
hdr.T = payloadFormat(buf[formatIndex])
hdr.Length = binary.BigEndian.Uint32(buf[formatIndex:])
pf = payloadFormat(buf[0])
length := binary.BigEndian.Uint32(buf[1:])
if hdr.Length == 0 {
return hdr.T, nil, nil
if length == 0 {
return pf, nil, nil
}
msg = make([]byte, int(hdr.Length))
msg = make([]byte, int(length))
if _, err := io.ReadFull(p.s, msg); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return 0, nil, err
}
return hdr.T, msg, nil
return pf, msg, nil
}
// encode serializes msg and prepends the message header. If msg is nil, it
// generates the message header of 0 message length.
func encode(c Codec, msg interface{}, pf payloadFormat) ([]byte, error) {
var buf bytes.Buffer
// Write message fixed header.
// Write message into the fixed header.
buf.WriteByte(uint8(pf))
var b []byte
var length uint32
var length int
if msg != nil {
var err error
// TODO(zhaoq): optimize to reduce memory alloc and copying.
@ -182,10 +177,13 @@ func encode(c Codec, msg interface{}, pf payloadFormat) ([]byte, error) {
if err != nil {
return nil, err
}
length = uint32(len(b))
length = len(b)
}
if length > math.MaxUint32 {
return nil, Errorf(codes.InvalidArgument, "grpc: message too large (%d bytes)", length)
}
var szHdr [4]byte
binary.BigEndian.PutUint32(szHdr[:], length)
binary.BigEndian.PutUint32(szHdr[:], uint32(length))
buf.Write(szHdr[:])
buf.Write(b)
return buf.Bytes(), nil

View File

@ -47,6 +47,7 @@ import (
)
func TestSimpleParsing(t *testing.T) {
bigMsg := bytes.Repeat([]byte{'x'}, 1<<24)
for _, test := range []struct {
// input
p []byte
@ -60,6 +61,8 @@ func TestSimpleParsing(t *testing.T) {
{[]byte{0, 0, 0, 0, 1, 'a'}, nil, []byte{'a'}, compressionNone},
{[]byte{1, 0}, io.ErrUnexpectedEOF, nil, compressionNone},
{[]byte{0, 0, 0, 0, 10, 'a'}, io.ErrUnexpectedEOF, nil, compressionNone},
// Check that messages with length >= 2^24 are parsed.
{append([]byte{0, 1, 0, 0, 0}, bigMsg...), nil, bigMsg, compressionNone},
} {
buf := bytes.NewReader(test.p)
parser := &parser{buf}