Compare commits

...

11 Commits

Author SHA1 Message Date
Alessio Treglia
13eff7f7ed Merge pull request from GHSA-v24h-pjjv-mcp6
v0.33.3: advisory fix
2020-04-09 15:48:13 +02:00
Anton Kaliaev
62c1a1399c update changelog and version 2020-04-03 17:06:27 +04:00
Anton Kaliaev
9821af83e7 p2p: limit the number of incoming connections
to p2p.max_num_inbound_peers + len(p2p.unconditional_peer_ids)
2020-04-03 17:06:11 +04:00
Tess Rinearson
0c0cb9f200 mempool: reserve IDs in InitPeer instead of AddPeer 2020-04-03 17:05:43 +04:00
Anton Kaliaev
2544a5cf3a noop commit (#4553)
should force CircleCI to update the code cache

Found a cache from build 100135 at go-src-v1-b215e07ecdc6ad5c363c3b420580aa6b52913feb
Size: 65 MiB
Cached paths:
* /go/src/github.com/tendermint/tendermint/.git

because we have v0.33.1-dev3 tag, bump-semver.py script is thinking the
next tag should be v0.33.1, not v0.33.2:

if re.match('[0-9]+$',patch) is None:
  patchfound = re.match('([0-9]+)',patch)
patch = int(patchfound.group(1))
  else:
  patch = int(patch) + 1

  The last tag it finds is v0.33.1-dev3. So it hits the first branch. But
  the first branch only fetches the number, and doesn't increment it, so
  the result is v0.33.1.

  if there exists a patch tag like 1-dev3, then the next version should be
  0.33.1. But in this case, 0.33.1 has already been released.
2020-03-11 13:55:00 +01:00
Anton Kaliaev
b215e07ecd Merge pull request #4550 from tendermint/release/v0.33.2
Release/v0.33.2
2020-03-11 15:22:48 +04:00
Anton Kaliaev
ec9424c6ce Merge pull request #4402 from tendermint/release/v0.33.1
Release/v0.33.1
2020-02-14 12:56:26 +01:00
Alessio Treglia
30e5619ac0 Merge branch 'v0.33' into release/v0.33.1 2020-02-14 11:39:01 +00:00
Alessio Treglia
e62b21c9bd Merge pull request #4404 from tendermint/rc2/0.33.1
upgrade: update upgrade.md for protobuf changes
2020-02-13 16:42:25 +00:00
Marko Baricevic
42d8bc5124 upgrade: update upgrade.md for protobuf changes
Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2020-02-13 17:13:48 +01:00
Marko Baricevic
af99236105 release: 0.33
Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2020-01-15 12:45:10 +01:00
12 changed files with 161 additions and 11 deletions

View File

@@ -1,5 +1,66 @@
# Changelog
## v0.33.3
*April 6, 2020*
This security release fixes:
### Denial of service 1
Tendermint 0.33.2 and earlier does not limit P2P connection requests number.
For each p2p connection, Tendermint allocates ~0.5MB. Even though this
memory is garbage collected once the connection is terminated (due to duplicate
IP or reaching a maximum number of inbound peers), temporary memory spikes can
lead to OOM (Out-Of-Memory) exceptions.
Tendermint 0.33.3 (and 0.32.10) limits the total number of P2P incoming
connection requests to to `p2p.max_num_inbound_peers +
len(p2p.unconditional_peer_ids)`.
Notes:
- Tendermint does not rate limit P2P connection requests per IP (an attacker
can saturate all the inbound slots);
- Tendermint does not rate limit HTTP(S) requests. If you expose any RPC
endpoints to the public, please make sure to put in place some protection
(https://www.nginx.com/blog/rate-limiting-nginx/). We may implement this in
the future ([\#1696](https://github.com/tendermint/tendermint/issues/1696)).
### Denial of service 2
Tendermint 0.33.2 and earlier does not reclaim `activeID` of a peer after it's
removed in `Mempool` reactor. This does not happen all the time. It only
happens when a connection fails (for any reason) before the Peer is created and
added to all reactors. `RemovePeer` is therefore called before `AddPeer`, which
leads to always growing memory (`activeIDs` map). The `activeIDs` map has a
maximum size of 65535 and the node will panic if this map reaches the maximum.
An attacker can create a lot of connection attempts (exploiting Denial of
service 1), which ultimately will lead to the node panicking.
Tendermint 0.33.3 (and 0.32.10) claims `activeID` for a peer in `InitPeer`,
which is executed before `MConnection` is started.
Notes:
- `InitPeer` function was added to all reactors to combat a similar issue -
[\#3338](https://github.com/tendermint/tendermint/issues/3338);
- Denial of service 2 is independent of Denial of service 1 and can be executed
without it.
**All clients are recommended to upgrade**
Special thanks to [fudongbai](https://hackerone.com/fudongbai) for finding
and reporting this.
Friendly reminder, we have a [bug bounty
program](https://hackerone.com/tendermint).
### SECURITY:
- [mempool] Reserve IDs in InitPeer instead of AddPeer (@tessr)
- [p2p] Limit the number of incoming connections (@melekes)
## v0.33.2
*March 11, 2020*

View File

@@ -1,4 +1,4 @@
## v0.33.3
## v0.33.4
\*\*

View File

@@ -7,9 +7,13 @@ a newer version of Tendermint Core.
<Overview>
## v0.33.1
This release is compatible with the previous version. The only change that is required is if you are fetching the protobuf files for application use.
### Protobuf Changes
When upgrading to version <version #> you will have to fetch the `third_party` directory along with the updated proto files.
When upgrading to version 0.33.1 you will have to fetch the `third_party` directory along with the updated proto files.
## v0.33.0

View File

@@ -57,7 +57,7 @@ func NewReactor(consensusState *State, fastSync bool, options ...ReactorOption)
metrics: NopMetrics(),
}
conR.updateFastSyncingMetric()
conR.BaseReactor = *p2p.NewBaseReactor("Reactor", conR)
conR.BaseReactor = *p2p.NewBaseReactor("Consensus", conR)
for _, option := range options {
option(conR)

View File

@@ -34,7 +34,7 @@ func NewReactor(evpool *Pool) *Reactor {
evR := &Reactor{
evpool: evpool,
}
evR.BaseReactor = *p2p.NewBaseReactor("Reactor", evR)
evR.BaseReactor = *p2p.NewBaseReactor("Evidence", evR)
return evR
}

View File

@@ -47,7 +47,7 @@ type mempoolIDs struct {
activeIDs map[uint16]struct{} // used to check if a given peerID key is used, the value doesn't matter
}
// Reserve searches for the next unused ID and assignes it to the
// Reserve searches for the next unused ID and assigns it to the
// peer.
func (ids *mempoolIDs) ReserveForPeer(peer p2p.Peer) {
ids.mtx.Lock()
@@ -110,10 +110,16 @@ func NewReactor(config *cfg.MempoolConfig, mempool *CListMempool) *Reactor {
mempool: mempool,
ids: newMempoolIDs(),
}
memR.BaseReactor = *p2p.NewBaseReactor("Reactor", memR)
memR.BaseReactor = *p2p.NewBaseReactor("Mempool", memR)
return memR
}
// InitPeer implements Reactor by creating a state for the peer.
func (memR *Reactor) InitPeer(peer p2p.Peer) p2p.Peer {
memR.ids.ReserveForPeer(peer)
return peer
}
// SetLogger sets the Logger on the reactor and the underlying mempool.
func (memR *Reactor) SetLogger(l log.Logger) {
memR.Logger = l
@@ -142,7 +148,6 @@ func (memR *Reactor) GetChannels() []*p2p.ChannelDescriptor {
// AddPeer implements Reactor.
// It starts a broadcast routine ensuring all txs are forwarded to the given peer.
func (memR *Reactor) AddPeer(peer p2p.Peer) {
memR.ids.ReserveForPeer(peer)
go memR.broadcastTxRoutine(peer)
}

View File

@@ -223,3 +223,21 @@ func TestMempoolIDsPanicsIfNodeRequestsOvermaxActiveIDs(t *testing.T) {
ids.ReserveForPeer(peer)
})
}
func TestDontExhaustMaxActiveIDs(t *testing.T) {
config := cfg.TestConfig()
const N = 1
reactors := makeAndConnectReactors(config, N)
defer func() {
for _, r := range reactors {
r.Stop()
}
}()
reactor := reactors[0]
for i := 0; i < maxActiveIDs+1; i++ {
peer := mock.NewPeer(nil)
reactor.Receive(MempoolChannel, peer, []byte{0x1, 0x2, 0x3})
reactor.AddPeer(peer)
}
}

View File

@@ -467,6 +467,11 @@ func createTransport(
}
p2p.MultiplexTransportConnFilters(connFilters...)(transport)
// Limit the number of incoming connections.
max := config.P2P.MaxNumInboundPeers + len(splitAndTrimEmpty(config.P2P.UnconditionalPeerIDs, ",", " "))
p2p.MultiplexTransportMaxIncomingConnections(max)(transport)
return transport, peerFilters
}

View File

@@ -137,7 +137,7 @@ func NewReactor(b AddrBook, config *ReactorConfig) *Reactor {
lastReceivedRequests: cmap.NewCMap(),
crawlPeerInfos: make(map[p2p.ID]crawlPeerInfo),
}
r.BaseReactor = *p2p.NewBaseReactor("Reactor", r)
r.BaseReactor = *p2p.NewBaseReactor("PEX", r)
return r
}

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/pkg/errors"
"golang.org/x/net/netutil"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/p2p/conn"
@@ -122,11 +123,18 @@ func MultiplexTransportResolver(resolver IPResolver) MultiplexTransportOption {
return func(mt *MultiplexTransport) { mt.resolver = resolver }
}
// MultiplexTransportMaxIncomingConnections sets the maximum number of
// simultaneous connections (incoming). Default: 0 (unlimited)
func MultiplexTransportMaxIncomingConnections(n int) MultiplexTransportOption {
return func(mt *MultiplexTransport) { mt.maxIncomingConnections = n }
}
// MultiplexTransport accepts and dials tcp connections and upgrades them to
// multiplexed peers.
type MultiplexTransport struct {
netAddr NetAddress
listener net.Listener
netAddr NetAddress
listener net.Listener
maxIncomingConnections int // see MaxIncomingConnections
acceptc chan accept
closec chan struct{}
@@ -240,6 +248,10 @@ func (mt *MultiplexTransport) Listen(addr NetAddress) error {
return err
}
if mt.maxIncomingConnections > 0 {
ln = netutil.LimitListener(ln, mt.maxIncomingConnections)
}
mt.netAddr = addr
mt.listener = ln

View File

@@ -5,6 +5,7 @@ import (
"math/rand"
"net"
"reflect"
"strings"
"testing"
"time"
@@ -134,6 +135,50 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) {
}
}
func TestTransportMultiplexMaxIncomingConnections(t *testing.T) {
mt := newMultiplexTransport(
emptyNodeInfo(),
NodeKey{
PrivKey: ed25519.GenPrivKey(),
},
)
id := mt.nodeKey.ID()
MultiplexTransportMaxIncomingConnections(0)(mt)
addr, err := NewNetAddressString(IDAddressString(id, "127.0.0.1:0"))
if err != nil {
t.Fatal(err)
}
if err := mt.Listen(*addr); err != nil {
t.Fatal(err)
}
errc := make(chan error)
go func() {
addr := NewNetAddress(id, mt.listener.Addr())
_, err := addr.Dial()
if err != nil {
errc <- err
return
}
close(errc)
}()
if err := <-errc; err != nil {
t.Errorf("connection failed: %v", err)
}
_, err = mt.Accept(peerConfig{})
if err == nil || !strings.Contains(err.Error(), "connection reset by peer") {
t.Errorf("expected connection reset by peer error, got %v", err)
}
}
func TestTransportMultiplexAcceptMultiple(t *testing.T) {
mt := testSetupMultiplexTransport(t)
laddr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr())

View File

@@ -20,7 +20,7 @@ const (
// Must be a string because scripts like dist.sh read this file.
// XXX: Don't change the name of this variable or you will break
// automation :)
TMCoreSemVer = "0.33.2"
TMCoreSemVer = "0.33.3"
// ABCISemVer is the semantic version of the ABCI library
ABCISemVer = "0.16.1"