Skip to content

Commit cdfe9a3

Browse files
holimanfjl
authored andcommitted
eth, les: add sanity checks for unbounded block fields (#19573)
This PR adds some hardening in the lower levels of the protocol stack, to bail early on invalid data. Primarily, attacks that this PR protects against are on the "annoyance"-level, which would otherwise write a couple of megabytes of data into the log output, which is a bit resource intensive.
1 parent 5bc9ccf commit cdfe9a3

File tree

6 files changed

+57
-5
lines changed

6 files changed

+57
-5
lines changed

core/types/block.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package types
1919

2020
import (
2121
"encoding/binary"
22+
"fmt"
2223
"io"
2324
"math/big"
2425
"reflect"
@@ -110,6 +111,25 @@ func (h *Header) Size() common.StorageSize {
110111
return headerSize + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen())/8)
111112
}
112113

114+
// SanityCheck checks a few basic things -- these checks are way beyond what
115+
// any 'sane' production values should hold, and can mainly be used to prevent
116+
// that the unbounded fields are stuffed with junk data to add processing
117+
// overhead
118+
func (h *Header) SanityCheck() error {
119+
if h.Number != nil && !h.Number.IsUint64() {
120+
return fmt.Errorf("too large block number: bitlen %d", h.Number.BitLen())
121+
}
122+
if h.Difficulty != nil {
123+
if diffLen := h.Difficulty.BitLen(); diffLen > 80 {
124+
return fmt.Errorf("too large block difficulty: bitlen %d", diffLen)
125+
}
126+
}
127+
if eLen := len(h.Extra); eLen > 100*1024 {
128+
return fmt.Errorf("too large block extradata: size %d", eLen)
129+
}
130+
return nil
131+
}
132+
113133
func rlpHash(x interface{}) (h common.Hash) {
114134
hw := sha3.NewLegacyKeccak256()
115135
rlp.Encode(hw, x)
@@ -316,6 +336,12 @@ func (b *Block) Size() common.StorageSize {
316336
return common.StorageSize(c)
317337
}
318338

339+
// SanityCheck can be used to prevent that unbounded fields are
340+
// stuffed with junk data to add processing overhead
341+
func (b *Block) SanityCheck() error {
342+
return b.header.SanityCheck()
343+
}
344+
319345
type writeCounter common.StorageSize
320346

321347
func (c *writeCounter) Write(b []byte) (int, error) {

eth/fetcher/fetcher.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ func (f *Fetcher) forgetHash(hash common.Hash) {
685685
// Remove all pending announces and decrement DOS counters
686686
for _, announce := range f.announced[hash] {
687687
f.announces[announce.origin]--
688-
if f.announces[announce.origin] == 0 {
688+
if f.announces[announce.origin] <= 0 {
689689
delete(f.announces, announce.origin)
690690
}
691691
}
@@ -696,7 +696,7 @@ func (f *Fetcher) forgetHash(hash common.Hash) {
696696
// Remove any pending fetches and decrement the DOS counters
697697
if announce := f.fetching[hash]; announce != nil {
698698
f.announces[announce.origin]--
699-
if f.announces[announce.origin] == 0 {
699+
if f.announces[announce.origin] <= 0 {
700700
delete(f.announces, announce.origin)
701701
}
702702
delete(f.fetching, hash)
@@ -705,7 +705,7 @@ func (f *Fetcher) forgetHash(hash common.Hash) {
705705
// Remove any pending completion requests and decrement the DOS counters
706706
for _, announce := range f.fetched[hash] {
707707
f.announces[announce.origin]--
708-
if f.announces[announce.origin] == 0 {
708+
if f.announces[announce.origin] <= 0 {
709709
delete(f.announces, announce.origin)
710710
}
711711
}
@@ -714,7 +714,7 @@ func (f *Fetcher) forgetHash(hash common.Hash) {
714714
// Remove any pending completions and decrement the DOS counters
715715
if announce := f.completing[hash]; announce != nil {
716716
f.announces[announce.origin]--
717-
if f.announces[announce.origin] == 0 {
717+
if f.announces[announce.origin] <= 0 {
718718
delete(f.announces, announce.origin)
719719
}
720720
delete(f.completing, hash)

eth/handler.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,9 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
697697
if err := msg.Decode(&request); err != nil {
698698
return errResp(ErrDecode, "%v: %v", msg, err)
699699
}
700+
if err := request.sanityCheck(); err != nil {
701+
return err
702+
}
700703
request.Block.ReceivedAt = msg.ReceivedAt
701704
request.Block.ReceivedFrom = p
702705

eth/protocol.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,19 @@ type newBlockData struct {
173173
TD *big.Int
174174
}
175175

176+
// sanityCheck verifies that the values are reasonable, as a DoS protection
177+
func (request *newBlockData) sanityCheck() error {
178+
if err := request.Block.SanityCheck(); err != nil {
179+
return err
180+
}
181+
//TD at mainnet block #7753254 is 76 bits. If it becomes 100 million times
182+
// larger, it will still fit within 100 bits
183+
if tdlen := request.TD.BitLen(); tdlen > 100 {
184+
return fmt.Errorf("too large block TD: bitlen %d", tdlen)
185+
}
186+
return nil
187+
}
188+
176189
// blockBody represents the data content of a single block.
177190
type blockBody struct {
178191
Transactions []*types.Transaction // Transactions contained within a block

les/handler.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,9 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
442442
if err := msg.Decode(&req); err != nil {
443443
return errResp(ErrDecode, "%v: %v", msg, err)
444444
}
445-
445+
if err := req.sanityCheck(); err != nil {
446+
return err
447+
}
446448
update, size := req.Update.decode()
447449
if p.rejectUpdate(size) {
448450
return errResp(ErrRequestRejected, "")

les/protocol.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,14 @@ type announceData struct {
149149
Update keyValueList
150150
}
151151

152+
// sanityCheck verifies that the values are reasonable, as a DoS protection
153+
func (a *announceData) sanityCheck() error {
154+
if tdlen := a.Td.BitLen(); tdlen > 100 {
155+
return fmt.Errorf("too large block TD: bitlen %d", tdlen)
156+
}
157+
return nil
158+
}
159+
152160
// sign adds a signature to the block announcement by the given privKey
153161
func (a *announceData) sign(privKey *ecdsa.PrivateKey) {
154162
rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td})

0 commit comments

Comments
 (0)