rlp: reject non-minimal input strings
Input strings of length 1 containing a byte < 56 are non-minimal and should be encoded as a single byte instead. Reject such strings.
This commit is contained in:
parent
6e9f8035a1
commit
1e2c93aa2d
@ -305,10 +305,11 @@ func decodeByteSlice(s *Stream, val reflect.Value) error {
|
|||||||
return decodeListSlice(s, val, decodeUint)
|
return decodeListSlice(s, val, decodeUint)
|
||||||
}
|
}
|
||||||
b, err := s.Bytes()
|
b, err := s.Bytes()
|
||||||
if err == nil {
|
if err != nil {
|
||||||
val.SetBytes(b)
|
return wrapStreamError(err, val.Type())
|
||||||
}
|
}
|
||||||
return err
|
val.SetBytes(b)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeByteArray(s *Stream, val reflect.Value) error {
|
func decodeByteArray(s *Stream, val reflect.Value) error {
|
||||||
@ -333,6 +334,10 @@ func decodeByteArray(s *Stream, val reflect.Value) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
zero(val, int(size))
|
zero(val, int(size))
|
||||||
|
// Reject cases where single byte encoding should have been used.
|
||||||
|
if size == 1 && slice[0] < 56 {
|
||||||
|
return wrapStreamError(ErrCanonSize, val.Type())
|
||||||
|
}
|
||||||
case List:
|
case List:
|
||||||
return decodeListArray(s, val, decodeUint)
|
return decodeListArray(s, val, decodeUint)
|
||||||
}
|
}
|
||||||
@ -477,7 +482,7 @@ var (
|
|||||||
// Actual Errors
|
// Actual Errors
|
||||||
ErrExpectedString = errors.New("rlp: expected String or Byte")
|
ErrExpectedString = errors.New("rlp: expected String or Byte")
|
||||||
ErrExpectedList = errors.New("rlp: expected List")
|
ErrExpectedList = errors.New("rlp: expected List")
|
||||||
ErrCanonInt = errors.New("rlp: non-canonical (leading zero bytes) integer")
|
ErrCanonInt = errors.New("rlp: non-canonical integer format")
|
||||||
ErrCanonSize = errors.New("rlp: non-canonical size information")
|
ErrCanonSize = errors.New("rlp: non-canonical size information")
|
||||||
ErrElemTooLarge = errors.New("rlp: element is larger than containing list")
|
ErrElemTooLarge = errors.New("rlp: element is larger than containing list")
|
||||||
ErrValueTooLarge = errors.New("rlp: value size exceeds available input length")
|
ErrValueTooLarge = errors.New("rlp: value size exceeds available input length")
|
||||||
@ -576,6 +581,9 @@ func (s *Stream) Bytes() ([]byte, error) {
|
|||||||
if err = s.readFull(b); err != nil {
|
if err = s.readFull(b); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if size == 1 && b[0] < 56 {
|
||||||
|
return nil, ErrCanonSize
|
||||||
|
}
|
||||||
return b, nil
|
return b, nil
|
||||||
default:
|
default:
|
||||||
return nil, ErrExpectedString
|
return nil, ErrExpectedString
|
||||||
@ -631,11 +639,17 @@ func (s *Stream) uint(maxbits int) (uint64, error) {
|
|||||||
return 0, errUintOverflow
|
return 0, errUintOverflow
|
||||||
}
|
}
|
||||||
v, err := s.readUint(byte(size))
|
v, err := s.readUint(byte(size))
|
||||||
if err == ErrCanonSize {
|
switch {
|
||||||
|
case err == ErrCanonSize:
|
||||||
// Adjust error because we're not reading a size right now.
|
// Adjust error because we're not reading a size right now.
|
||||||
err = ErrCanonInt
|
return 0, ErrCanonInt
|
||||||
|
case err != nil:
|
||||||
|
return 0, err
|
||||||
|
case size > 0 && v < 56:
|
||||||
|
return 0, ErrCanonSize
|
||||||
|
default:
|
||||||
|
return v, nil
|
||||||
}
|
}
|
||||||
return v, err
|
|
||||||
default:
|
default:
|
||||||
return 0, ErrExpectedString
|
return 0, ErrExpectedString
|
||||||
}
|
}
|
||||||
|
@ -93,12 +93,16 @@ func TestStreamErrors(t *testing.T) {
|
|||||||
{"00", calls{"ListEnd"}, nil, errNotInList},
|
{"00", calls{"ListEnd"}, nil, errNotInList},
|
||||||
{"C401020304", calls{"List", "Uint", "ListEnd"}, nil, errNotAtEOL},
|
{"C401020304", calls{"List", "Uint", "ListEnd"}, nil, errNotAtEOL},
|
||||||
|
|
||||||
// Leading zero bytes are rejected when reading integers.
|
// Non-canonical integers (e.g. leading zero bytes).
|
||||||
{"00", calls{"Uint"}, nil, ErrCanonInt},
|
{"00", calls{"Uint"}, nil, ErrCanonInt},
|
||||||
{"820002", calls{"Uint"}, nil, ErrCanonInt},
|
{"820002", calls{"Uint"}, nil, ErrCanonInt},
|
||||||
|
{"8133", calls{"Uint"}, nil, ErrCanonSize},
|
||||||
|
{"8156", calls{"Uint"}, nil, nil},
|
||||||
|
|
||||||
// Size tags must use the smallest possible encoding.
|
// Size tags must use the smallest possible encoding.
|
||||||
// Leading zero bytes in the size tag are also rejected.
|
// Leading zero bytes in the size tag are also rejected.
|
||||||
|
{"8100", calls{"Uint"}, nil, ErrCanonSize},
|
||||||
|
{"8100", calls{"Bytes"}, nil, ErrCanonSize},
|
||||||
{"B800", calls{"Kind"}, withoutInputLimit, ErrCanonSize},
|
{"B800", calls{"Kind"}, withoutInputLimit, ErrCanonSize},
|
||||||
{"B90000", calls{"Kind"}, withoutInputLimit, ErrCanonSize},
|
{"B90000", calls{"Kind"}, withoutInputLimit, ErrCanonSize},
|
||||||
{"B90055", calls{"Kind"}, withoutInputLimit, ErrCanonSize},
|
{"B90055", calls{"Kind"}, withoutInputLimit, ErrCanonSize},
|
||||||
@ -112,7 +116,7 @@ func TestStreamErrors(t *testing.T) {
|
|||||||
{"", calls{"Kind"}, nil, io.EOF},
|
{"", calls{"Kind"}, nil, io.EOF},
|
||||||
{"", calls{"Uint"}, nil, io.EOF},
|
{"", calls{"Uint"}, nil, io.EOF},
|
||||||
{"", calls{"List"}, nil, io.EOF},
|
{"", calls{"List"}, nil, io.EOF},
|
||||||
{"8105", calls{"Uint", "Uint"}, nil, io.EOF},
|
{"8158", calls{"Uint", "Uint"}, nil, io.EOF},
|
||||||
{"C0", calls{"List", "ListEnd", "List"}, nil, io.EOF},
|
{"C0", calls{"List", "ListEnd", "List"}, nil, io.EOF},
|
||||||
|
|
||||||
// Input limit errors.
|
// Input limit errors.
|
||||||
@ -129,11 +133,11 @@ func TestStreamErrors(t *testing.T) {
|
|||||||
// down toward zero in Stream.remaining, reading too far can overflow
|
// down toward zero in Stream.remaining, reading too far can overflow
|
||||||
// remaining to a large value, effectively disabling the limit.
|
// remaining to a large value, effectively disabling the limit.
|
||||||
{"C40102030401", calls{"Raw", "Uint"}, withCustomInputLimit(5), io.EOF},
|
{"C40102030401", calls{"Raw", "Uint"}, withCustomInputLimit(5), io.EOF},
|
||||||
{"C4010203048102", calls{"Raw", "Uint"}, withCustomInputLimit(6), ErrValueTooLarge},
|
{"C4010203048158", calls{"Raw", "Uint"}, withCustomInputLimit(6), ErrValueTooLarge},
|
||||||
|
|
||||||
// Check that the same calls are fine without a limit.
|
// Check that the same calls are fine without a limit.
|
||||||
{"C40102030401", calls{"Raw", "Uint"}, withoutInputLimit, nil},
|
{"C40102030401", calls{"Raw", "Uint"}, withoutInputLimit, nil},
|
||||||
{"C4010203048102", calls{"Raw", "Uint"}, withoutInputLimit, nil},
|
{"C4010203048158", calls{"Raw", "Uint"}, withoutInputLimit, nil},
|
||||||
|
|
||||||
// Unexpected EOF. This only happens when there is
|
// Unexpected EOF. This only happens when there is
|
||||||
// no input limit, so the reader needs to be 'dumbed down'.
|
// no input limit, so the reader needs to be 'dumbed down'.
|
||||||
@ -295,13 +299,13 @@ var decodeTests = []decodeTest{
|
|||||||
// integers
|
// integers
|
||||||
{input: "05", ptr: new(uint32), value: uint32(5)},
|
{input: "05", ptr: new(uint32), value: uint32(5)},
|
||||||
{input: "80", ptr: new(uint32), value: uint32(0)},
|
{input: "80", ptr: new(uint32), value: uint32(0)},
|
||||||
{input: "8105", ptr: new(uint32), value: uint32(5)},
|
|
||||||
{input: "820505", ptr: new(uint32), value: uint32(0x0505)},
|
{input: "820505", ptr: new(uint32), value: uint32(0x0505)},
|
||||||
{input: "83050505", ptr: new(uint32), value: uint32(0x050505)},
|
{input: "83050505", ptr: new(uint32), value: uint32(0x050505)},
|
||||||
{input: "8405050505", ptr: new(uint32), value: uint32(0x05050505)},
|
{input: "8405050505", ptr: new(uint32), value: uint32(0x05050505)},
|
||||||
{input: "850505050505", ptr: new(uint32), error: "rlp: input string too long for uint32"},
|
{input: "850505050505", ptr: new(uint32), error: "rlp: input string too long for uint32"},
|
||||||
{input: "C0", ptr: new(uint32), error: "rlp: expected input string or byte for uint32"},
|
{input: "C0", ptr: new(uint32), error: "rlp: expected input string or byte for uint32"},
|
||||||
{input: "00", ptr: new(uint32), error: "rlp: non-canonical integer (leading zero bytes) for uint32"},
|
{input: "00", ptr: new(uint32), error: "rlp: non-canonical integer (leading zero bytes) for uint32"},
|
||||||
|
{input: "8105", ptr: new(uint32), error: "rlp: non-canonical size information for uint32"},
|
||||||
{input: "820004", ptr: new(uint32), error: "rlp: non-canonical integer (leading zero bytes) for uint32"},
|
{input: "820004", ptr: new(uint32), error: "rlp: non-canonical integer (leading zero bytes) for uint32"},
|
||||||
{input: "B8020004", ptr: new(uint32), error: "rlp: non-canonical size information for uint32"},
|
{input: "B8020004", ptr: new(uint32), error: "rlp: non-canonical size information for uint32"},
|
||||||
|
|
||||||
@ -319,10 +323,16 @@ var decodeTests = []decodeTest{
|
|||||||
// byte slices
|
// byte slices
|
||||||
{input: "01", ptr: new([]byte), value: []byte{1}},
|
{input: "01", ptr: new([]byte), value: []byte{1}},
|
||||||
{input: "80", ptr: new([]byte), value: []byte{}},
|
{input: "80", ptr: new([]byte), value: []byte{}},
|
||||||
|
|
||||||
{input: "8D6162636465666768696A6B6C6D", ptr: new([]byte), value: []byte("abcdefghijklm")},
|
{input: "8D6162636465666768696A6B6C6D", ptr: new([]byte), value: []byte("abcdefghijklm")},
|
||||||
{input: "C0", ptr: new([]byte), value: []byte{}},
|
{input: "C0", ptr: new([]byte), value: []byte{}},
|
||||||
{input: "C3010203", ptr: new([]byte), value: []byte{1, 2, 3}},
|
{input: "C3010203", ptr: new([]byte), value: []byte{1, 2, 3}},
|
||||||
|
|
||||||
|
{
|
||||||
|
input: "8105",
|
||||||
|
ptr: new([]byte),
|
||||||
|
error: "rlp: non-canonical size information for []uint8",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
input: "C3820102",
|
input: "C3820102",
|
||||||
ptr: new([]byte),
|
ptr: new([]byte),
|
||||||
@ -346,10 +356,15 @@ var decodeTests = []decodeTest{
|
|||||||
ptr: new([5]byte),
|
ptr: new([5]byte),
|
||||||
error: "rlp: input string too long for [5]uint8",
|
error: "rlp: input string too long for [5]uint8",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: "8105",
|
||||||
|
ptr: new([5]byte),
|
||||||
|
error: "rlp: non-canonical size information for [5]uint8",
|
||||||
|
},
|
||||||
|
|
||||||
// byte array reuse (should be zeroed)
|
// byte array reuse (should be zeroed)
|
||||||
{input: "850102030405", ptr: &sharedByteArray, value: [5]byte{1, 2, 3, 4, 5}},
|
{input: "850102030405", ptr: &sharedByteArray, value: [5]byte{1, 2, 3, 4, 5}},
|
||||||
{input: "8101", ptr: &sharedByteArray, value: [5]byte{1}}, // kind: String
|
{input: "01", ptr: &sharedByteArray, value: [5]byte{1}}, // kind: String
|
||||||
{input: "850102030405", ptr: &sharedByteArray, value: [5]byte{1, 2, 3, 4, 5}},
|
{input: "850102030405", ptr: &sharedByteArray, value: [5]byte{1, 2, 3, 4, 5}},
|
||||||
{input: "01", ptr: &sharedByteArray, value: [5]byte{1}}, // kind: Byte
|
{input: "01", ptr: &sharedByteArray, value: [5]byte{1}}, // kind: Byte
|
||||||
{input: "C3010203", ptr: &sharedByteArray, value: [5]byte{1, 2, 3, 0, 0}},
|
{input: "C3010203", ptr: &sharedByteArray, value: [5]byte{1, 2, 3, 0, 0}},
|
||||||
@ -369,9 +384,10 @@ var decodeTests = []decodeTest{
|
|||||||
// big ints
|
// big ints
|
||||||
{input: "01", ptr: new(*big.Int), value: big.NewInt(1)},
|
{input: "01", ptr: new(*big.Int), value: big.NewInt(1)},
|
||||||
{input: "89FFFFFFFFFFFFFFFFFF", ptr: new(*big.Int), value: veryBigInt},
|
{input: "89FFFFFFFFFFFFFFFFFF", ptr: new(*big.Int), value: veryBigInt},
|
||||||
{input: "820001", ptr: new(big.Int), error: "rlp: non-canonical integer (leading zero bytes) for *big.Int"},
|
|
||||||
{input: "10", ptr: new(big.Int), value: *big.NewInt(16)}, // non-pointer also works
|
{input: "10", ptr: new(big.Int), value: *big.NewInt(16)}, // non-pointer also works
|
||||||
{input: "C0", ptr: new(*big.Int), error: "rlp: expected input string or byte for *big.Int"},
|
{input: "C0", ptr: new(*big.Int), error: "rlp: expected input string or byte for *big.Int"},
|
||||||
|
{input: "820001", ptr: new(big.Int), error: "rlp: non-canonical integer (leading zero bytes) for *big.Int"},
|
||||||
|
{input: "8105", ptr: new(big.Int), error: "rlp: non-canonical size information for *big.Int"},
|
||||||
|
|
||||||
// structs
|
// structs
|
||||||
{input: "C0", ptr: new(simplestruct), value: simplestruct{0, ""}},
|
{input: "C0", ptr: new(simplestruct), value: simplestruct{0, ""}},
|
||||||
@ -404,7 +420,7 @@ var decodeTests = []decodeTest{
|
|||||||
{input: "80", ptr: new(*uint), value: (*uint)(nil)},
|
{input: "80", ptr: new(*uint), value: (*uint)(nil)},
|
||||||
{input: "C0", ptr: new(*uint), value: (*uint)(nil)},
|
{input: "C0", ptr: new(*uint), value: (*uint)(nil)},
|
||||||
{input: "07", ptr: new(*uint), value: uintp(7)},
|
{input: "07", ptr: new(*uint), value: uintp(7)},
|
||||||
{input: "8108", ptr: new(*uint), value: uintp(8)},
|
{input: "8158", ptr: new(*uint), value: uintp(0x58)},
|
||||||
{input: "C109", ptr: new(*[]uint), value: &[]uint{9}},
|
{input: "C109", ptr: new(*[]uint), value: &[]uint{9}},
|
||||||
{input: "C58403030303", ptr: new(*[][]byte), value: &[][]byte{{3, 3, 3, 3}}},
|
{input: "C58403030303", ptr: new(*[][]byte), value: &[][]byte{{3, 3, 3, 3}}},
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user