Skip to content

Commit 027d724

Browse files
committed
encoding/binary: read at most MaxVarintLen64 bytes in ReadUvarint
This CL ensures that ReadUvarint consumes only a limited amount of input (instead of an unbounded amount). On some inputs, ReadUvarint could read an arbitrary number of bytes before deciding to return an overflow error. After this CL, ReadUvarint returns that same overflow error sooner, after reading at most MaxVarintLen64 bytes. Fix authored by Robert Griesemer and Filippo Valsorda. Thanks to Diederik Loerakker, Jonny Rhea, Raúl Kripalani, and Preston Van Loon for reporting this. Fixes #40618 Fixes CVE-2020-16845 Change-Id: Ie0cb15972f14c38b7cf7af84c45c4ce54909bb8f Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/812099 Reviewed-by: Filippo Valsorda <[email protected]> Reviewed-on: https://go-review.googlesource.com/c/go/+/247120 Run-TryBot: Katie Hockman <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Alexander Rakoczy <[email protected]>
1 parent 6f08e89 commit 027d724

File tree

2 files changed

+15
-8
lines changed

2 files changed

+15
-8
lines changed

src/encoding/binary/varint.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,20 +106,21 @@ var overflow = errors.New("binary: varint overflows a 64-bit integer")
106106
func ReadUvarint(r io.ByteReader) (uint64, error) {
107107
var x uint64
108108
var s uint
109-
for i := 0; ; i++ {
109+
for i := 0; i < MaxVarintLen64; i++ {
110110
b, err := r.ReadByte()
111111
if err != nil {
112112
return x, err
113113
}
114114
if b < 0x80 {
115-
if i > 9 || i == 9 && b > 1 {
115+
if i == 9 && b > 1 {
116116
return x, overflow
117117
}
118118
return x | uint64(b)<<s, nil
119119
}
120120
x |= uint64(b&0x7f) << s
121121
s += 7
122122
}
123+
return x, overflow
123124
}
124125

125126
// ReadVarint reads an encoded signed integer from r and returns it as an int64.

src/encoding/binary/varint_test.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,21 +121,27 @@ func TestBufferTooSmall(t *testing.T) {
121121
}
122122
}
123123

124-
func testOverflow(t *testing.T, buf []byte, n0 int, err0 error) {
124+
func testOverflow(t *testing.T, buf []byte, x0 uint64, n0 int, err0 error) {
125125
x, n := Uvarint(buf)
126126
if x != 0 || n != n0 {
127127
t.Errorf("Uvarint(%v): got x = %d, n = %d; want 0, %d", buf, x, n, n0)
128128
}
129129

130-
x, err := ReadUvarint(bytes.NewReader(buf))
131-
if x != 0 || err != err0 {
132-
t.Errorf("ReadUvarint(%v): got x = %d, err = %s; want 0, %s", buf, x, err, err0)
130+
r := bytes.NewReader(buf)
131+
len := r.Len()
132+
x, err := ReadUvarint(r)
133+
if x != x0 || err != err0 {
134+
t.Errorf("ReadUvarint(%v): got x = %d, err = %s; want %d, %s", buf, x, err, x0, err0)
135+
}
136+
if read := len - r.Len(); read > MaxVarintLen64 {
137+
t.Errorf("ReadUvarint(%v): read more than MaxVarintLen64 bytes, got %d", buf, read)
133138
}
134139
}
135140

136141
func TestOverflow(t *testing.T) {
137-
testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x2}, -10, overflow)
138-
testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1, 0, 0}, -13, overflow)
142+
testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x2}, 0, -10, overflow)
143+
testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1, 0, 0}, 0, -13, overflow)
144+
testOverflow(t, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 1<<64-1, 0, overflow) // 11 bytes, should overflow
139145
}
140146

141147
func TestNonCanonicalZero(t *testing.T) {

0 commit comments

Comments
 (0)