Skip to content

Commit

Permalink
eth, les: add sanity checks for unbounded block fields (ethereum#19573)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
holiman authored and wanwiset25 committed Jun 19, 2024
1 parent 1bdc0e7 commit 58b6709
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 5 deletions.
25 changes: 25 additions & 0 deletions core/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,25 @@ func (h *Header) Size() common.StorageSize {
return common.StorageSize(unsafe.Sizeof(*h)) + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8)
}

// SanityCheck checks a few basic things -- these checks are way beyond what
// any 'sane' production values should hold, and can mainly be used to prevent
// that the unbounded fields are stuffed with junk data to add processing
// overhead
func (h *Header) SanityCheck() error {
if h.Number != nil && !h.Number.IsUint64() {
return fmt.Errorf("too large block number: bitlen %d", h.Number.BitLen())
}
if h.Difficulty != nil {
if diffLen := h.Difficulty.BitLen(); diffLen > 80 {
return fmt.Errorf("too large block difficulty: bitlen %d", diffLen)
}
}
if eLen := len(h.Extra); eLen > 100*1024 {
return fmt.Errorf("too large block extradata: size %d", eLen)
}
return nil
}

// Body is a simple (mutable, non-safe) data container for storing and moving
// a block's data contents (transactions and uncles) together.
type Body struct {
Expand Down Expand Up @@ -369,6 +388,12 @@ func (b *Block) Size() common.StorageSize {
return common.StorageSize(c)
}

// SanityCheck can be used to prevent that unbounded fields are
// stuffed with junk data to add processing overhead
func (b *Block) SanityCheck() error {
return b.header.SanityCheck()
}

type writeCounter common.StorageSize

func (c *writeCounter) Write(b []byte) (int, error) {
Expand Down
8 changes: 4 additions & 4 deletions eth/fetcher/block_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,7 @@ func (f *BlockFetcher) forgetHash(hash common.Hash) {
// Remove all pending announces and decrement DOS counters
for _, announce := range f.announced[hash] {
f.announces[announce.origin]--
if f.announces[announce.origin] == 0 {
if f.announces[announce.origin] <= 0 {
delete(f.announces, announce.origin)
}
}
Expand All @@ -815,7 +815,7 @@ func (f *BlockFetcher) forgetHash(hash common.Hash) {
// Remove any pending fetches and decrement the DOS counters
if announce := f.fetching[hash]; announce != nil {
f.announces[announce.origin]--
if f.announces[announce.origin] == 0 {
if f.announces[announce.origin] <= 0 {
delete(f.announces, announce.origin)
}
delete(f.fetching, hash)
Expand All @@ -824,7 +824,7 @@ func (f *BlockFetcher) forgetHash(hash common.Hash) {
// Remove any pending completion requests and decrement the DOS counters
for _, announce := range f.fetched[hash] {
f.announces[announce.origin]--
if f.announces[announce.origin] == 0 {
if f.announces[announce.origin] <= 0 {
delete(f.announces, announce.origin)
}
}
Expand All @@ -833,7 +833,7 @@ func (f *BlockFetcher) forgetHash(hash common.Hash) {
// Remove any pending completions and decrement the DOS counters
if announce := f.completing[hash]; announce != nil {
f.announces[announce.origin]--
if f.announces[announce.origin] == 0 {
if f.announces[announce.origin] <= 0 {
delete(f.announces, announce.origin)
}
delete(f.completing, hash)
Expand Down
3 changes: 3 additions & 0 deletions eth/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,9 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if err := msg.Decode(&request); err != nil {
return errResp(ErrDecode, "%v: %v", msg, err)
}
if err := request.sanityCheck(); err != nil {
return err
}
request.Block.ReceivedAt = msg.ReceivedAt
request.Block.ReceivedFrom = p

Expand Down
13 changes: 13 additions & 0 deletions eth/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,19 @@ type newBlockData struct {
TD *big.Int
}

// sanityCheck verifies that the values are reasonable, as a DoS protection
func (request *newBlockData) sanityCheck() error {
if err := request.Block.SanityCheck(); err != nil {
return err
}
//TD at mainnet block #7753254 is 76 bits. If it becomes 100 million times
// larger, it will still fit within 100 bits
if tdlen := request.TD.BitLen(); tdlen > 100 {
return fmt.Errorf("too large block TD: bitlen %d", tdlen)
}
return nil
}

// blockBody represents the data content of a single block.
type blockBody struct {
Transactions []*types.Transaction // Transactions contained within a block
Expand Down
4 changes: 3 additions & 1 deletion les/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,9 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if err := msg.Decode(&req); err != nil {
return errResp(ErrDecode, "%v: %v", msg, err)
}

if err := req.sanityCheck(); err != nil {
return err
}
if p.requestAnnounceType == announceTypeSigned {
if err := req.checkSignature(p.ID()); err != nil {
p.Log().Trace("Invalid announcement signature", "err", err)
Expand Down
8 changes: 8 additions & 0 deletions les/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ type announceData struct {
Update keyValueList
}

// sanityCheck verifies that the values are reasonable, as a DoS protection
func (a *announceData) sanityCheck() error {
if tdlen := a.Td.BitLen(); tdlen > 100 {
return fmt.Errorf("too large block TD: bitlen %d", tdlen)
}
return nil
}

// sign adds a signature to the block announcement by the given privKey
func (a *announceData) sign(privKey *ecdsa.PrivateKey) {
rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td})
Expand Down

0 comments on commit 58b6709

Please sign in to comment.