Skip to content

Commit

Permalink
vm: add two proof verifier to fix the vulnerability in range proof (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
unclezoro committed Oct 11, 2022
1 parent f3fd0f8 commit cb131fa
Show file tree
Hide file tree
Showing 15 changed files with 338 additions and 36 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## v1.1.16
* [\#1121](https://github.com/bnb-chain/bsc/pull/1121) vm: add two proof verifier to fix the vulnerability in range proof

## v1.1.15
* [\#1109](https://github.com/bnb-chain/bsc/pull/1109) nanofork: block exploitation accounts and suspend cross chain bridge related precompile contracts

Expand Down
6 changes: 6 additions & 0 deletions accounts/abi/bind/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2023,6 +2023,12 @@ func TestGolangBindings(t *testing.T) {
if out, err := replacer.CombinedOutput(); err != nil {
t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out)
}

replacer = exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/tendermint/tendermint@v0.0.0", "-replace", "github.com/tendermint/tendermint=github.com/bnb-chain/tendermint@v0.31.12") // Repo root
replacer.Dir = pkg
if out, err := replacer.CombinedOutput(); err != nil {
t.Fatalf("failed to replace tendermint dependency to bnb-chain source: %v\n%s", err, out)
}
tidier := exec.Command(gocmd, "mod", "tidy")
tidier.Dir = pkg
if out, err := tidier.CombinedOutput(); err != nil {
Expand Down
69 changes: 69 additions & 0 deletions core/systemcontracts/upgrade.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion core/types/blacklist.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package types

import "github.com/ethereum/go-ethereum/common"

// This is introduced because of the Tendermint IAVL Merkel Proof verification exploitation.
// This is introduced because of the Tendermint IAVL Merkle Proof verification exploitation.
var NanoBlackList = []common.Address{
common.HexToAddress("0x489A8756C18C0b8B24EC2a2b9FF3D4d447F79BEc"),
common.HexToAddress("0xFd6042Df3D74ce9959922FeC559d7995F3933c55"),
Expand Down
22 changes: 22 additions & 0 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,21 @@ var PrecompiledContractsIsNano = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{101}): &iavlMerkleProofValidateNano{},
}

var PrecompiledContractsIsMoran = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},

common.BytesToAddress([]byte{100}): &tmHeaderValidate{},
common.BytesToAddress([]byte{101}): &iavlMerkleProofValidateMoran{},
}

// PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum
// contracts used in the Berlin release.
var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{
Expand Down Expand Up @@ -125,6 +140,7 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
}

var (
PrecompiledAddressesMoran []common.Address
PrecompiledAddressesNano []common.Address
PrecompiledAddressesBerlin []common.Address
PrecompiledAddressesIstanbul []common.Address
Expand All @@ -148,11 +164,17 @@ func init() {
for k := range PrecompiledContractsIsNano {
PrecompiledAddressesNano = append(PrecompiledAddressesNano, k)
}

for k := range PrecompiledContractsIsMoran {
PrecompiledAddressesMoran = append(PrecompiledAddressesMoran, k)
}
}

// ActivePrecompiles returns the precompiles enabled with the current configuration.
func ActivePrecompiles(rules params.Rules) []common.Address {
switch {
case rules.IsMoran:
return PrecompiledAddressesMoran
case rules.IsNano:
return PrecompiledAddressesNano
case rules.IsBerlin:
Expand Down
124 changes: 103 additions & 21 deletions core/vm/contracts_lightclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import (
"encoding/binary"
"fmt"

"github.com/tendermint/iavl"
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common"

"github.com/ethereum/go-ethereum/core/vm/lightclient"
"github.com/ethereum/go-ethereum/params"
)
Expand Down Expand Up @@ -93,8 +97,10 @@ func (c *tmHeaderValidate) Run(input []byte) (result []byte, err error) {

//------------------------------------------------------------------------------------------------------------------------------------------------

// tmHeaderValidate implemented as a native contract.
type iavlMerkleProofValidate struct{}
// iavlMerkleProofValidate implemented as a native contract.
type iavlMerkleProofValidate struct {
basicIavlMerkleProofValidate
}

func (c *iavlMerkleProofValidate) RequiredGas(input []byte) uint64 {
return params.IAVLMerkleProofValidateGas
Expand All @@ -104,7 +110,54 @@ func (c *iavlMerkleProofValidate) RequiredGas(input []byte) uint64 {
// | payload length | payload |
// | 32 bytes | |
func (c *iavlMerkleProofValidate) Run(input []byte) (result []byte, err error) {
//return nil, fmt.Errorf("suspend")
return c.basicIavlMerkleProofValidate.Run(input)
}

// tmHeaderValidate implemented as a native contract.
type tmHeaderValidateNano struct{}

func (c *tmHeaderValidateNano) RequiredGas(input []byte) uint64 {
return params.TendermintHeaderValidateGas
}

func (c *tmHeaderValidateNano) Run(input []byte) (result []byte, err error) {
return nil, fmt.Errorf("suspend")
}

type iavlMerkleProofValidateNano struct{}

func (c *iavlMerkleProofValidateNano) RequiredGas(_ []byte) uint64 {
return params.IAVLMerkleProofValidateGas
}

func (c *iavlMerkleProofValidateNano) Run(_ []byte) (result []byte, err error) {
return nil, fmt.Errorf("suspend")
}

//------------------------------------------------------------------------------------------------------------------------------------------------
type iavlMerkleProofValidateMoran struct {
basicIavlMerkleProofValidate
}

func (c *iavlMerkleProofValidateMoran) RequiredGas(_ []byte) uint64 {
return params.IAVLMerkleProofValidateGas
}

func (c *iavlMerkleProofValidateMoran) Run(input []byte) (result []byte, err error) {
c.basicIavlMerkleProofValidate.verifiers = []merkle.ProofOpVerifier{
forbiddenAbsenceOpVerifier,
singleValueOpVerifier,
multiStoreOpVerifier,
forbiddenSimpleValueOpVerifier,
}
return c.basicIavlMerkleProofValidate.Run(input)
}

type basicIavlMerkleProofValidate struct {
verifiers []merkle.ProofOpVerifier
}

func (c *basicIavlMerkleProofValidate) Run(input []byte) (result []byte, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("internal error: %v\n", r)
Expand All @@ -124,7 +177,7 @@ func (c *iavlMerkleProofValidate) Run(input []byte) (result []byte, err error) {
if err != nil {
return nil, err
}

kvmp.SetVerifiers(c.verifiers)
valid := kvmp.Validate()
if !valid {
return nil, fmt.Errorf("invalid merkle proof")
Expand All @@ -135,27 +188,56 @@ func (c *iavlMerkleProofValidate) Run(input []byte) (result []byte, err error) {
return result, nil
}

// tmHeaderValidate implemented as a native contract.
type tmHeaderValidateNano struct{}

func (c *tmHeaderValidateNano) RequiredGas(input []byte) uint64 {
return params.TendermintHeaderValidateGas
func forbiddenAbsenceOpVerifier(op merkle.ProofOperator) error {
if op == nil {
return nil
}
if _, ok := op.(iavl.IAVLAbsenceOp); ok {
return cmn.NewError("absence proof suspend")
}
return nil
}

func (c *tmHeaderValidateNano) Run(input []byte) (result []byte, err error) {
return nil, fmt.Errorf("suspend")
func forbiddenSimpleValueOpVerifier(op merkle.ProofOperator) error {
if op == nil {
return nil
}
if _, ok := op.(merkle.SimpleValueOp); ok {
return cmn.NewError("simple value proof suspend")
}
return nil
}

//------------------------------------------------------------------------------------------------------------------------------------------------
type iavlMerkleProofValidateNano struct{}

func (c *iavlMerkleProofValidateNano) RequiredGas(input []byte) uint64 {
return params.IAVLMerkleProofValidateGas
func multiStoreOpVerifier(op merkle.ProofOperator) error {
if op == nil {
return nil
}
if mop, ok := op.(lightclient.MultiStoreProofOp); ok {
storeNames := make(map[string]bool, len(mop.Proof.StoreInfos))
for _, store := range mop.Proof.StoreInfos {
if exist := storeNames[store.Name]; exist {
return cmn.NewError("duplicated store")
} else {
storeNames[store.Name] = true
}
}
}
return nil
}

// input:
// | payload length | payload |
// | 32 bytes | |
func (c *iavlMerkleProofValidateNano) Run(input []byte) (result []byte, err error) {
return nil, fmt.Errorf("suspend")
func singleValueOpVerifier(op merkle.ProofOperator) error {
if op == nil {
return nil
}
if valueOp, ok := op.(iavl.IAVLValueOp); ok {
if len(valueOp.Proof.Leaves) != 1 {
return cmn.NewError("range proof suspended")
}
for _, innerNode := range valueOp.Proof.LeftPath {
if len(innerNode.Right) > 0 && len(innerNode.Left) > 0 {
return cmn.NewError("both right and left hash exit!")
}
}
}
return nil
}
Loading

0 comments on commit cb131fa

Please sign in to comment.