encoding/protodelim: fix handling of io.EOF

Before this change, when encountering an io.EOF after reading at least
one byte, the zero value byte was — incorrectly — appended to sizeBuf,
and the io.EOF was ignored, resulting in a complete varint (0 has no
continuation bit), which in turn resulted in incorrect unmarshalling.

Change-Id: If06d45039d998eaddf91d0864814bb31d4cb7ae0
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/505555
Reviewed-by: Lasse Folger <lassefolger@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
Michael Stapelberg 2023-06-23 14:49:52 +02:00 committed by Michael Stapelberg
parent fc47fdd3d3
commit 59a8581684
2 changed files with 22 additions and 1 deletions

View File

@ -96,7 +96,11 @@ func (o UnmarshalOptions) UnmarshalFrom(r Reader, m proto.Message) error {
sizeBuf := sizeArr[:0] sizeBuf := sizeArr[:0]
for i := range sizeArr { for i := range sizeArr {
b, err := r.ReadByte() b, err := r.ReadByte()
if err != nil && (err != io.EOF || i == 0) { if err != nil {
// Immediate EOF is unexpected.
if err == io.EOF && i != 0 {
break
}
return err return err
} }
sizeBuf = append(sizeBuf, b) sizeBuf = append(sizeBuf, b)

View File

@ -7,6 +7,7 @@ package protodelim_test
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/binary"
"errors" "errors"
"io" "io"
"testing" "testing"
@ -202,3 +203,19 @@ func TestUnmarshalFrom_UnexpectedEOF(t *testing.T) {
t.Errorf("protodelim.UnmarshalFrom(size-only buf, _) = %v, want %v", got, want) t.Errorf("protodelim.UnmarshalFrom(size-only buf, _) = %v, want %v", got, want)
} }
} }
func TestUnmarshalFrom_PrematureHeader(t *testing.T) {
var data = []byte{128} // continuation bit set
err := protodelim.UnmarshalFrom(bytes.NewReader(data[:]), nil)
if got, want := err, io.ErrUnexpectedEOF; !errors.Is(got, want) {
t.Errorf("protodelim.UnmarshalFrom(%#v, nil) = %#v; want = %#v", data, got, want)
}
}
func TestUnmarshalFrom_InvalidVarint(t *testing.T) {
var data = bytes.Repeat([]byte{128}, 2*binary.MaxVarintLen64) // continuation bit set
err := protodelim.UnmarshalFrom(bytes.NewReader(data[:]), nil)
if err == nil {
t.Errorf("protodelim.UnmarshalFrom unexpectedly did not error on invalid varint")
}
}