mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
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 CVE-2020-16845 Change-Id: Ie0cb15972f14c38b7cf7af84c45c4ce54909bb8f Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/812099 Reviewed-by: Filippo Valsorda <valsorda@google.com> Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/812324
135 lines
3.8 KiB
Go
135 lines
3.8 KiB
Go
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package binary
|
|
|
|
// This file implements "varint" encoding of 64-bit integers.
|
|
// The encoding is:
|
|
// - unsigned integers are serialized 7 bits at a time, starting with the
|
|
// least significant bits
|
|
// - the most significant bit (msb) in each output byte indicates if there
|
|
// is a continuation byte (msb = 1)
|
|
// - signed integers are mapped to unsigned integers using "zig-zag"
|
|
// encoding: Positive values x are written as 2*x + 0, negative values
|
|
// are written as 2*(^x) + 1; that is, negative numbers are complemented
|
|
// and whether to complement is encoded in bit 0.
|
|
//
|
|
// Design note:
|
|
// At most 10 bytes are needed for 64-bit values. The encoding could
|
|
// be more dense: a full 64-bit value needs an extra byte just to hold bit 63.
|
|
// Instead, the msb of the previous byte could be used to hold bit 63 since we
|
|
// know there can't be more than 64 bits. This is a trivial improvement and
|
|
// would reduce the maximum encoding length to 9 bytes. However, it breaks the
|
|
// invariant that the msb is always the "continuation bit" and thus makes the
|
|
// format incompatible with a varint encoding for larger numbers (say 128-bit).
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
)
|
|
|
|
// MaxVarintLenN is the maximum length of a varint-encoded N-bit integer.
|
|
const (
|
|
MaxVarintLen16 = 3
|
|
MaxVarintLen32 = 5
|
|
MaxVarintLen64 = 10
|
|
)
|
|
|
|
// PutUvarint encodes a uint64 into buf and returns the number of bytes written.
|
|
// If the buffer is too small, PutUvarint will panic.
|
|
func PutUvarint(buf []byte, x uint64) int {
|
|
i := 0
|
|
for x >= 0x80 {
|
|
buf[i] = byte(x) | 0x80
|
|
x >>= 7
|
|
i++
|
|
}
|
|
buf[i] = byte(x)
|
|
return i + 1
|
|
}
|
|
|
|
// Uvarint decodes a uint64 from buf and returns that value and the
|
|
// number of bytes read (> 0). If an error occurred, the value is 0
|
|
// and the number of bytes n is <= 0 meaning:
|
|
//
|
|
// n == 0: buf too small
|
|
// n < 0: value larger than 64 bits (overflow)
|
|
// and -n is the number of bytes read
|
|
//
|
|
func Uvarint(buf []byte) (uint64, int) {
|
|
var x uint64
|
|
var s uint
|
|
for i, b := range buf {
|
|
if b < 0x80 {
|
|
if i > 9 || i == 9 && b > 1 {
|
|
return 0, -(i + 1) // overflow
|
|
}
|
|
return x | uint64(b)<<s, i + 1
|
|
}
|
|
x |= uint64(b&0x7f) << s
|
|
s += 7
|
|
}
|
|
return 0, 0
|
|
}
|
|
|
|
// PutVarint encodes an int64 into buf and returns the number of bytes written.
|
|
// If the buffer is too small, PutVarint will panic.
|
|
func PutVarint(buf []byte, x int64) int {
|
|
ux := uint64(x) << 1
|
|
if x < 0 {
|
|
ux = ^ux
|
|
}
|
|
return PutUvarint(buf, ux)
|
|
}
|
|
|
|
// Varint decodes an int64 from buf and returns that value and the
|
|
// number of bytes read (> 0). If an error occurred, the value is 0
|
|
// and the number of bytes n is <= 0 with the following meaning:
|
|
//
|
|
// n == 0: buf too small
|
|
// n < 0: value larger than 64 bits (overflow)
|
|
// and -n is the number of bytes read
|
|
//
|
|
func Varint(buf []byte) (int64, int) {
|
|
ux, n := Uvarint(buf) // ok to continue in presence of error
|
|
x := int64(ux >> 1)
|
|
if ux&1 != 0 {
|
|
x = ^x
|
|
}
|
|
return x, n
|
|
}
|
|
|
|
var overflow = errors.New("binary: varint overflows a 64-bit integer")
|
|
|
|
// ReadUvarint reads an encoded unsigned integer from r and returns it as a uint64.
|
|
func ReadUvarint(r io.ByteReader) (uint64, error) {
|
|
var x uint64
|
|
var s uint
|
|
for i := 0; i < MaxVarintLen64; i++ {
|
|
b, err := r.ReadByte()
|
|
if err != nil {
|
|
return x, err
|
|
}
|
|
if b < 0x80 {
|
|
if i == 9 && b > 1 {
|
|
return x, overflow
|
|
}
|
|
return x | uint64(b)<<s, nil
|
|
}
|
|
x |= uint64(b&0x7f) << s
|
|
s += 7
|
|
}
|
|
return x, overflow
|
|
}
|
|
|
|
// ReadVarint reads an encoded signed integer from r and returns it as an int64.
|
|
func ReadVarint(r io.ByteReader) (int64, error) {
|
|
ux, err := ReadUvarint(r) // ok to continue in presence of error
|
|
x := int64(ux >> 1)
|
|
if ux&1 != 0 {
|
|
x = ^x
|
|
}
|
|
return x, err
|
|
}
|