Skip to content

Commit 3a90ca0

Browse files
authored
Merge pull request #2655 from CortexFoundation/dev
rlp: validate and cache element count in RawList
2 parents f94b01e + b107b1b commit 3a90ca0

4 files changed

Lines changed: 77 additions & 25 deletions

File tree

rlp/encode.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func EncodeToRawList[T any](val []T) (RawList[T], error) {
119119
bytes := make([]byte, contentSize+9)
120120
offset := 9 - headsize(uint64(contentSize))
121121
buf.copyTo(bytes[offset:])
122-
return RawList[T]{enc: bytes}, nil
122+
return RawList[T]{enc: bytes, length: len(val)}, nil
123123
}
124124

125125
type listhead struct {

rlp/iterator.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,6 @@ func (it *Iterator) Next() bool {
6565
return true
6666
}
6767

68-
// Count returns the remaining number of items.
69-
// Note this is O(n) and the result may be incorrect if the list data is invalid.
70-
// The returned count is always an upper bound on the remaining items
71-
// that will be visited by the iterator.
72-
func (it *Iterator) Count() int {
73-
count, _ := CountValues(it.data)
74-
return count
75-
}
76-
7768
// Value returns the current value.
7869
func (it *Iterator) Value() []byte {
7970
return it.next

rlp/raw.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ type RawList[T any] struct {
4444
// The implementation code mostly works with the Content method because it
4545
// returns something valid either way.
4646
enc []byte
47+
48+
// length holds the number of items in the list.
49+
length int
4750
}
4851

4952
// Content returns the RLP-encoded data of the list.
@@ -87,7 +90,14 @@ func (r *RawList[T]) DecodeRLP(s *Stream) error {
8790
if err := s.readFull(enc[9:]); err != nil {
8891
return err
8992
}
90-
*r = RawList[T]{enc: enc}
93+
n, err := CountValues(enc[9:])
94+
if err != nil {
95+
if err == ErrValueTooLarge {
96+
return ErrElemTooLarge
97+
}
98+
return err
99+
}
100+
*r = RawList[T]{enc: enc, length: n}
91101
return nil
92102
}
93103

@@ -105,20 +115,14 @@ func (r *RawList[T]) Items() ([]T, error) {
105115

106116
// Len returns the number of items in the list.
107117
func (r *RawList[T]) Len() int {
108-
len, _ := CountValues(r.Content())
109-
return len
118+
return r.length
110119
}
111120

112121
// Size returns the encoded size of the list.
113122
func (r *RawList[T]) Size() uint64 {
114123
return ListSize(uint64(len(r.Content())))
115124
}
116125

117-
// Empty returns true if the list contains no items.
118-
func (r *RawList[T]) Empty() bool {
119-
return len(r.Content()) == 0
120-
}
121-
122126
// ContentIterator returns an iterator over the content of the list.
123127
// Note the offsets returned by iterator.Offset are relative to the
124128
// Content bytes of the list.
@@ -142,15 +146,26 @@ func (r *RawList[T]) Append(item T) error {
142146
end := prevEnd + eb.size()
143147
r.enc = slices.Grow(r.enc, eb.size())[:end]
144148
eb.copyTo(r.enc[prevEnd:end])
149+
r.length++
145150
return nil
146151
}
147152

148153
// AppendRaw adds an encoded item to the list.
149-
func (r *RawList[T]) AppendRaw(b []byte) {
154+
// The given byte slice must contain exactly one RLP value.
155+
func (r *RawList[T]) AppendRaw(b []byte) error {
156+
_, tagsize, contentsize, err := readKind(b)
157+
if err != nil {
158+
return err
159+
}
160+
if tagsize+contentsize != uint64(len(b)) {
161+
return fmt.Errorf("rlp: input has trailing bytes in AppendRaw")
162+
}
150163
if r.enc == nil {
151164
r.enc = make([]byte, 9)
152165
}
153166
r.enc = append(r.enc, b...)
167+
r.length++
168+
return nil
154169
}
155170

156171
// StringSize returns the encoded size of a string.

rlp/raw_test.go

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,6 @@ func (test rawListTest[T]) run(t *testing.T) {
6767
// check iterator
6868
it := rl.ContentIterator()
6969
i := 0
70-
if count := it.Count(); count != test.length {
71-
t.Fatalf("iterator has wrong Count %d, want %d", count, test.length)
72-
}
7370
for it.Next() {
7471
var item T
7572
if err := DecodeBytes(it.Value(), &item); err != nil {
@@ -153,9 +150,6 @@ func TestRawListEmpty(t *testing.T) {
153150
if !bytes.Equal(b, unhex("C0")) {
154151
t.Fatalf("empty RawList has wrong encoding %x", b)
155152
}
156-
if !rl.Empty() {
157-
t.Fatal("list should be Empty")
158-
}
159153
if rl.Len() != 0 {
160154
t.Fatalf("empty list has Len %d", rl.Len())
161155
}
@@ -225,6 +219,58 @@ func TestRawListAppend(t *testing.T) {
225219
}
226220
}
227221

222+
func TestRawListAppendRaw(t *testing.T) {
223+
var rl RawList[uint64]
224+
225+
if err := rl.AppendRaw(unhex("01")); err != nil {
226+
t.Fatal("AppendRaw(01) failed:", err)
227+
}
228+
if err := rl.AppendRaw(unhex("820102")); err != nil {
229+
t.Fatal("AppendRaw(820102) failed:", err)
230+
}
231+
if rl.Len() != 2 {
232+
t.Fatalf("wrong Len %d after valid appends", rl.Len())
233+
}
234+
235+
if err := rl.AppendRaw(nil); err == nil {
236+
t.Fatal("AppendRaw(nil) should fail")
237+
}
238+
if err := rl.AppendRaw(unhex("0102")); err == nil {
239+
t.Fatal("AppendRaw(0102) should fail due to trailing bytes")
240+
}
241+
if err := rl.AppendRaw(unhex("8201")); err == nil {
242+
t.Fatal("AppendRaw(8201) should fail due to truncated value")
243+
}
244+
if rl.Len() != 2 {
245+
t.Fatalf("wrong Len %d after invalid appends, want 2", rl.Len())
246+
}
247+
}
248+
249+
func TestRawListDecodeInvalid(t *testing.T) {
250+
tests := []struct {
251+
input string
252+
err error
253+
}{
254+
// Single item with non-canonical size (0x81 wrapping byte <= 0x7F).
255+
{input: "C28142", err: ErrCanonSize},
256+
// Single item claiming more bytes than available in the list.
257+
{input: "C484020202", err: ErrElemTooLarge},
258+
// Two items, second has non-canonical size.
259+
{input: "C3018142", err: ErrCanonSize},
260+
// Two items, second claims more bytes than remain in the list.
261+
{input: "C401830202", err: ErrElemTooLarge},
262+
// Item is a sub-list whose declared size exceeds available bytes.
263+
{input: "C3C40102", err: ErrElemTooLarge},
264+
}
265+
for _, test := range tests {
266+
var rl RawList[RawValue]
267+
err := DecodeBytes(unhex(test.input), &rl)
268+
if !errors.Is(err, test.err) {
269+
t.Errorf("input %s: error mismatch: got %v, want %v", test.input, err, test.err)
270+
}
271+
}
272+
}
273+
228274
func TestCountValues(t *testing.T) {
229275
tests := []struct {
230276
input string // note: spaces in input are stripped by unhex

0 commit comments

Comments
 (0)