mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-06 05:25:35 +00:00
p2p/conn: check for channel id overflow before processing receive msg (#6522)
Per tendermint spec, each Channel has a globally unique byte id, which is mapped to uint8 in Go. However, the proto PacketMsg.ChannelID field is declared as int32, and when receive the packet, we cast it to a byte without checking for possible overflow. That leads to a malform packet with invalid channel id is sent successfully. To fix it, we just add a check for possible overflow, and return invalid channel id error. Fixed #6521
This commit is contained in:
@@ -625,8 +625,9 @@ FOR_LOOP:
|
||||
// never block
|
||||
}
|
||||
case *tmp2p.Packet_PacketMsg:
|
||||
channel, ok := c.channelsIdx[byte(pkt.PacketMsg.ChannelID)]
|
||||
if !ok || channel == nil {
|
||||
channelID := byte(pkt.PacketMsg.ChannelID)
|
||||
channel, ok := c.channelsIdx[channelID]
|
||||
if pkt.PacketMsg.ChannelID < 0 || pkt.PacketMsg.ChannelID > math.MaxUint8 || !ok || channel == nil {
|
||||
err := fmt.Errorf("unknown channel %X", pkt.PacketMsg.ChannelID)
|
||||
c.Logger.Debug("Connection failed @ recvRoutine", "conn", c, "err", err)
|
||||
c.stopForError(err)
|
||||
@@ -642,9 +643,9 @@ FOR_LOOP:
|
||||
break FOR_LOOP
|
||||
}
|
||||
if msgBytes != nil {
|
||||
c.Logger.Debug("Received bytes", "chID", pkt.PacketMsg.ChannelID, "msgBytes", msgBytes)
|
||||
c.Logger.Debug("Received bytes", "chID", channelID, "msgBytes", msgBytes)
|
||||
// NOTE: This means the reactor.Receive runs in the same thread as the p2p recv routine
|
||||
c.onReceive(byte(pkt.PacketMsg.ChannelID), msgBytes)
|
||||
c.onReceive(channelID, msgBytes)
|
||||
}
|
||||
default:
|
||||
err := fmt.Errorf("unknown message type %v", reflect.TypeOf(packet))
|
||||
|
||||
@@ -553,6 +553,36 @@ func TestConnVectors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMConnectionChannelOverflow(t *testing.T) {
|
||||
chOnErr := make(chan struct{})
|
||||
chOnRcv := make(chan struct{})
|
||||
|
||||
mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr)
|
||||
t.Cleanup(stopAll(t, mconnClient, mconnServer))
|
||||
|
||||
mconnServer.onReceive = func(chID byte, msgBytes []byte) {
|
||||
chOnRcv <- struct{}{}
|
||||
}
|
||||
|
||||
client := mconnClient.conn
|
||||
protoWriter := protoio.NewDelimitedWriter(client)
|
||||
|
||||
var packet = tmp2p.PacketMsg{
|
||||
ChannelID: 0x01,
|
||||
EOF: true,
|
||||
Data: []byte(`42`),
|
||||
}
|
||||
_, err := protoWriter.WriteMsg(mustWrapPacket(&packet))
|
||||
require.NoError(t, err)
|
||||
assert.True(t, expectSend(chOnRcv))
|
||||
|
||||
packet.ChannelID = int32(1025)
|
||||
_, err = protoWriter.WriteMsg(mustWrapPacket(&packet))
|
||||
require.NoError(t, err)
|
||||
assert.False(t, expectSend(chOnRcv))
|
||||
|
||||
}
|
||||
|
||||
type stopper interface {
|
||||
Stop() error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user