eth: SEC-29 eth wire protocol decoding invalid message data crashes client
- add validate method to types.Block - validate after Decode -> error - add tests for NewBlockMsg
This commit is contained in:
		| @@ -1,6 +1,7 @@ | ||||
| package eth | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"math/big" | ||||
| 	"os" | ||||
| @@ -63,6 +64,10 @@ func (self *testChainManager) GetBlockHashesFromHash(hash common.Hash, amount ui | ||||
| func (self *testChainManager) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) { | ||||
| 	if self.status != nil { | ||||
| 		td, currentBlock, genesisBlock = self.status() | ||||
| 	} else { | ||||
| 		td = common.Big1 | ||||
| 		currentBlock = common.Hash{1} | ||||
| 		genesisBlock = common.Hash{2} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| @@ -163,14 +168,29 @@ func (self *ethProtocolTester) run() { | ||||
| 	self.quit <- err | ||||
| } | ||||
|  | ||||
| func (self *ethProtocolTester) handshake(t *testing.T, mock bool) { | ||||
| 	td, currentBlock, genesis := self.chainManager.Status() | ||||
| 	// first outgoing msg should be StatusMsg. | ||||
| 	err := p2p.ExpectMsg(self, StatusMsg, &statusMsgData{ | ||||
| 		ProtocolVersion: ProtocolVersion, | ||||
| 		NetworkId:       NetworkId, | ||||
| 		TD:              *td, | ||||
| 		CurrentBlock:    currentBlock, | ||||
| 		GenesisBlock:    genesis, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("incorrect outgoing status: %v", err) | ||||
| 	} | ||||
| 	if mock { | ||||
| 		go p2p.Send(self, StatusMsg, &statusMsgData{ProtocolVersion, NetworkId, *td, currentBlock, genesis}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestStatusMsgErrors(t *testing.T) { | ||||
| 	logInit() | ||||
| 	eth := newEth(t) | ||||
| 	td := common.Big1 | ||||
| 	currentBlock := common.Hash{1} | ||||
| 	genesis := common.Hash{2} | ||||
| 	eth.chainManager.status = func() (*big.Int, common.Hash, common.Hash) { return td, currentBlock, genesis } | ||||
| 	go eth.run() | ||||
| 	td, currentBlock, genesis := eth.chainManager.Status() | ||||
|  | ||||
| 	tests := []struct { | ||||
| 		code          uint64 | ||||
| @@ -182,31 +202,20 @@ func TestStatusMsgErrors(t *testing.T) { | ||||
| 			wantErrorCode: ErrNoStatusMsg, | ||||
| 		}, | ||||
| 		{ | ||||
| 			code: StatusMsg, data: statusMsgData{10, NetworkId, td, currentBlock, genesis}, | ||||
| 			code: StatusMsg, data: statusMsgData{10, NetworkId, *td, currentBlock, genesis}, | ||||
| 			wantErrorCode: ErrProtocolVersionMismatch, | ||||
| 		}, | ||||
| 		{ | ||||
| 			code: StatusMsg, data: statusMsgData{ProtocolVersion, 999, td, currentBlock, genesis}, | ||||
| 			code: StatusMsg, data: statusMsgData{ProtocolVersion, 999, *td, currentBlock, genesis}, | ||||
| 			wantErrorCode: ErrNetworkIdMismatch, | ||||
| 		}, | ||||
| 		{ | ||||
| 			code: StatusMsg, data: statusMsgData{ProtocolVersion, NetworkId, td, currentBlock, common.Hash{3}}, | ||||
| 			code: StatusMsg, data: statusMsgData{ProtocolVersion, NetworkId, *td, currentBlock, common.Hash{3}}, | ||||
| 			wantErrorCode: ErrGenesisBlockMismatch, | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, test := range tests { | ||||
| 		// first outgoing msg should be StatusMsg. | ||||
| 		err := p2p.ExpectMsg(eth, StatusMsg, &statusMsgData{ | ||||
| 			ProtocolVersion: ProtocolVersion, | ||||
| 			NetworkId:       NetworkId, | ||||
| 			TD:              td, | ||||
| 			CurrentBlock:    currentBlock, | ||||
| 			GenesisBlock:    genesis, | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("incorrect outgoing status: %v", err) | ||||
| 		} | ||||
|  | ||||
| 		eth.handshake(t, false) | ||||
| 		// the send call might hang until reset because | ||||
| 		// the protocol might not read the payload. | ||||
| 		go p2p.Send(eth, test.code, test.data) | ||||
| @@ -216,3 +225,73 @@ func TestStatusMsgErrors(t *testing.T) { | ||||
| 		go eth.run() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestNewBlockMsg(t *testing.T) { | ||||
| 	logInit() | ||||
| 	eth := newEth(t) | ||||
| 	eth.blockPool.addBlock = func(block *types.Block, peerId string) (err error) { | ||||
| 		fmt.Printf("Add Block: %v\n", block) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	var disconnected bool | ||||
| 	eth.blockPool.removePeer = func(peerId string) { | ||||
| 		fmt.Printf("peer <%s> is disconnected\n", peerId) | ||||
| 		disconnected = true | ||||
| 	} | ||||
|  | ||||
| 	go eth.run() | ||||
|  | ||||
| 	eth.handshake(t, true) | ||||
| 	err := p2p.ExpectMsg(eth, TxMsg, []interface{}{}) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("transactions expected, got %v", err) | ||||
| 	} | ||||
|  | ||||
| 	var tds = make(chan *big.Int) | ||||
| 	eth.blockPool.addPeer = func(td *big.Int, currentBlock common.Hash, peerId string, requestHashes func(common.Hash) error, requestBlocks func([]common.Hash) error, peerError func(*errs.Error)) (best bool, suspended bool) { | ||||
| 		tds <- td | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	var delay = 1 * time.Second | ||||
| 	// eth.reset() | ||||
| 	block := types.NewBlock(common.Hash{1}, common.Address{1}, common.Hash{1}, common.Big1, 1, "extra") | ||||
|  | ||||
| 	go p2p.Send(eth, NewBlockMsg, &newBlockMsgData{Block: block}) | ||||
| 	timer := time.After(delay) | ||||
|  | ||||
| 	select { | ||||
| 	case td := <-tds: | ||||
| 		if td.Cmp(common.Big0) != 0 { | ||||
| 			t.Errorf("incorrect td %v, expected %v", td, common.Big0) | ||||
| 		} | ||||
| 	case <-timer: | ||||
| 		t.Errorf("no td recorded after %v", delay) | ||||
| 		return | ||||
| 	case err := <-eth.quit: | ||||
| 		t.Errorf("no error expected, got %v", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	go p2p.Send(eth, NewBlockMsg, &newBlockMsgData{block, common.Big2}) | ||||
| 	timer = time.After(delay) | ||||
|  | ||||
| 	select { | ||||
| 	case td := <-tds: | ||||
| 		if td.Cmp(common.Big2) != 0 { | ||||
| 			t.Errorf("incorrect td %v, expected %v", td, common.Big2) | ||||
| 		} | ||||
| 	case <-timer: | ||||
| 		t.Errorf("no td recorded after %v", delay) | ||||
| 		return | ||||
| 	case err := <-eth.quit: | ||||
| 		t.Errorf("no error expected, got %v", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	go p2p.Send(eth, NewBlockMsg, []interface{}{}) | ||||
| 	// Block.DecodeRLP: validation failed: header is nil | ||||
| 	eth.checkError(ErrDecode, delay) | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user