#37148 [BC-Insight] `wantedPeerDials()` branch will never be executed

Submitted on Nov 26th 2024 at 19:59:03 UTC by @br0nz3p1ck4x3 for Attackathon | Ethereum Protocol

  • Report ID: #37148

  • Report Type: Blockchain/DLT

  • Report severity: Insight

  • Target: https://github.com/prysmaticlabs/prysm

  • Impacts:

    • (Specifications) A bug in specifications with no direct impact on client implementations

Description

Hey Triagers, Ethereum Foundation, Prysm!

We encountered the following during our research and we think it can add value to the Prysm team.

Description

Inside prysm/beacon-chain/p2p/discovery.go::listenForNodes() we can find the following:

func (s *Service) listenForNewNodes() {
//..
			if s.isPeerAtLimit(false /* inbound */) {
				// Pause the main loop for a period to stop looking
				// for new peers.
				log.Trace("Not looking for peers, at peer limit")
				time.Sleep(pollingPeriod)
				continue
			}
			wantedCount := s.wantedPeerDials()
			if wantedCount == 0 {
				log.Trace("Not looking for peers, at peer limit")
				time.Sleep(pollingPeriod)
				continue
			}
//..
}

There is a problem here though in the logic.

To showcase this, we will first explain it with arbitrary values. After reading this, please run the Proof of Concept attached to the report.

Alright.

If we look at isPeerAtLimit(), we see the following:

Let's take the following arbitrary values that maximize the numbers such that the return statement will return true:

  • numOfConns = 9

  • maxPeers = 10

  • activePeers = 9

These values will evaluate to true in this return statement:

  • activePeers >= maxPeers || numOfConns >= maxPeers

Now, we go back to listenForNewNodes():

Now, wantedPeerDials() is called. If we look at the implementation:

The problem here is the following if-statement:

  • if maxPeers > activePeers {

As we saw in isPeerAtLimit(), which was called before wantedPeerDials(), maxPeers will always be bigger than activePeers. If it was not bigger than activePeers, it would enter the if-statement and continue:

Thus, wantedPeerDials() will always return a non-nil value and thus, this if-statement inside listenForNewNodes() will never be executed:

Proof of Concept

Proof of Concept

  • cd prysm/beacon-chain/p2p

  • vim discovery_test.go Paste the following test in this file:

  • Run using:

    • go test -timeout 30s -run ^TestPoc

The output will be:

As we can see, the only way to get 0wanted peers is for activePeers == maxPeers, but as shown, this can never happen because the for-loop inside listenForNewNodes() will return early because of:

Consider removing the wantedPeerDials() usage since it can not be invoked right now.

Was this helpful?