common/math: check for 256 bit overflow in ParseBig

This is supposed to prevent silent overflow/truncation of values in the
genesis block JSON. Without this check, a genesis block that set a
balance larger than 256 bits would lead to weird behaviour in the VM.
This commit is contained in:
Felix Lange 2017-02-22 16:14:09 +01:00
parent 8fd6e8043a
commit eb96864658
9 changed files with 47 additions and 38 deletions

View file

@ -78,8 +78,8 @@ func (self DirectoryFlag) Apply(set *flag.FlagSet) {
}) })
} }
// BigFlag is a command line flag that accepts big integers in decimal // BigFlag is a command line flag that accepts 256 bit big integers in decimal or
// or hexadecimal syntax. // hexadecimal syntax.
type BigFlag struct { type BigFlag struct {
Name string Name string
Value *big.Int Value *big.Int
@ -97,7 +97,7 @@ func (b *bigValue) String() string {
} }
func (b *bigValue) Set(s string) error { func (b *bigValue) Set(s string) error {
int, ok := math.ParseBig(s) int, ok := math.ParseBig256(s)
if !ok { if !ok {
return errors.New("invalid integer syntax") return errors.New("invalid integer syntax")
} }

View file

@ -28,23 +28,30 @@ var (
MaxBig256 = new(big.Int).Set(tt256m1) MaxBig256 = new(big.Int).Set(tt256m1)
) )
// ParseBig parses s as a big integer in decimal or hexadecimal syntax. // ParseBig256 parses s as a 256 bit integer in decimal or hexadecimal syntax.
// Leading zeros are accepted. The empty string parses as zero. // Leading zeros are accepted. The empty string parses as zero.
func ParseBig(s string) (*big.Int, bool) { func ParseBig256(s string) (*big.Int, bool) {
if s == "" { if s == "" {
return new(big.Int), true return new(big.Int), true
} }
var bigint *big.Int
var ok bool
if len(s) >= 2 && (s[:2] == "0x" || s[:2] == "0X") { if len(s) >= 2 && (s[:2] == "0x" || s[:2] == "0X") {
return new(big.Int).SetString(s[2:], 16) bigint, ok = new(big.Int).SetString(s[2:], 16)
} else {
bigint, ok = new(big.Int).SetString(s, 10)
} }
return new(big.Int).SetString(s, 10) if ok && bigint.BitLen() > 256 {
bigint, ok = nil, false
}
return bigint, ok
} }
// MustParseBig parses s as a big integer and panics if the string is invalid. // MustParseBig parses s as a 256 bit big integer and panics if the string is invalid.
func MustParseBig(s string) *big.Int { func MustParseBig256(s string) *big.Int {
v, ok := ParseBig(s) v, ok := ParseBig256(s)
if !ok { if !ok {
panic("invalid integer: " + s) panic("invalid 256 bit integer: " + s)
} }
return v return v
} }

View file

@ -22,7 +22,7 @@ import (
"testing" "testing"
) )
func TestParseBig(t *testing.T) { func TestParseBig256(t *testing.T) {
tests := []struct { tests := []struct {
input string input string
num *big.Int num *big.Int
@ -42,9 +42,11 @@ func TestParseBig(t *testing.T) {
// Invalid syntax: // Invalid syntax:
{"abcdef", nil, false}, {"abcdef", nil, false},
{"0xgg", nil, false}, {"0xgg", nil, false},
// Larger than 256 bits:
{"115792089237316195423570985008687907853269984665640564039457584007913129639936", nil, false},
} }
for _, test := range tests { for _, test := range tests {
num, ok := ParseBig(test.input) num, ok := ParseBig256(test.input)
if ok != test.ok { if ok != test.ok {
t.Errorf("ParseBig(%q) -> ok = %t, want %t", test.input, ok, test.ok) t.Errorf("ParseBig(%q) -> ok = %t, want %t", test.input, ok, test.ok)
continue continue
@ -55,13 +57,13 @@ func TestParseBig(t *testing.T) {
} }
} }
func TestMustParseBig(t *testing.T) { func TestMustParseBig256(t *testing.T) {
defer func() { defer func() {
if recover() == nil { if recover() == nil {
t.Error("MustParseBig should've panicked") t.Error("MustParseBig should've panicked")
} }
}() }()
MustParseBig("ggg") MustParseBig256("ggg")
} }
func TestBigMax(t *testing.T) { func TestBigMax(t *testing.T) {
@ -183,8 +185,8 @@ func TestExp(t *testing.T) {
{base: big.NewInt(1), exponent: big.NewInt(0), result: big.NewInt(1)}, {base: big.NewInt(1), exponent: big.NewInt(0), result: big.NewInt(1)},
{base: big.NewInt(1), exponent: big.NewInt(1), result: big.NewInt(1)}, {base: big.NewInt(1), exponent: big.NewInt(1), result: big.NewInt(1)},
{base: big.NewInt(1), exponent: big.NewInt(2), result: big.NewInt(1)}, {base: big.NewInt(1), exponent: big.NewInt(2), result: big.NewInt(1)},
{base: big.NewInt(3), exponent: big.NewInt(144), result: MustParseBig("507528786056415600719754159741696356908742250191663887263627442114881")}, {base: big.NewInt(3), exponent: big.NewInt(144), result: MustParseBig256("507528786056415600719754159741696356908742250191663887263627442114881")},
{base: big.NewInt(2), exponent: big.NewInt(255), result: MustParseBig("57896044618658097711785492504343953926634992332820282019728792003956564819968")}, {base: big.NewInt(2), exponent: big.NewInt(255), result: MustParseBig256("57896044618658097711785492504343953926634992332820282019728792003956564819968")},
} }
for _, test := range tests { for _, test := range tests {
if result := Exp(test.base, test.exponent); result.Cmp(test.result) != 0 { if result := Exp(test.base, test.exponent); result.Cmp(test.result) != 0 {

View file

@ -55,10 +55,10 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
} }
d.ParentTimestamp = math.MustParseUint64(ext.ParentTimestamp) d.ParentTimestamp = math.MustParseUint64(ext.ParentTimestamp)
d.ParentDifficulty = math.MustParseBig(ext.ParentDifficulty) d.ParentDifficulty = math.MustParseBig256(ext.ParentDifficulty)
d.CurrentTimestamp = math.MustParseUint64(ext.CurrentTimestamp) d.CurrentTimestamp = math.MustParseUint64(ext.CurrentTimestamp)
d.CurrentBlocknumber = math.MustParseBig(ext.CurrentBlocknumber) d.CurrentBlocknumber = math.MustParseBig256(ext.CurrentBlocknumber)
d.CurrentDifficulty = math.MustParseBig(ext.CurrentDifficulty) d.CurrentDifficulty = math.MustParseBig256(ext.CurrentDifficulty)
return nil return nil
} }

View file

@ -68,7 +68,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
// creating with empty hash always works // creating with empty hash always works
statedb, _ := state.New(common.Hash{}, chainDb) statedb, _ := state.New(common.Hash{}, chainDb)
for addr, account := range genesis.Alloc { for addr, account := range genesis.Alloc {
balance, ok := math.ParseBig(account.Balance) balance, ok := math.ParseBig256(account.Balance)
if !ok { if !ok {
return nil, fmt.Errorf("invalid balance for account %s: %q", addr, account.Balance) return nil, fmt.Errorf("invalid balance for account %s: %q", addr, account.Balance)
} }
@ -87,7 +87,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
} }
root, stateBatch := statedb.CommitBatch(false) root, stateBatch := statedb.CommitBatch(false)
difficulty, ok := math.ParseBig(genesis.Difficulty) difficulty, ok := math.ParseBig256(genesis.Difficulty)
if !ok { if !ok {
return nil, fmt.Errorf("invalid difficulty: %q", genesis.Difficulty) return nil, fmt.Errorf("invalid difficulty: %q", genesis.Difficulty)
} }
@ -99,7 +99,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
if !ok { if !ok {
return nil, fmt.Errorf("invalid nonce: %q", genesis.Nonce) return nil, fmt.Errorf("invalid nonce: %q", genesis.Nonce)
} }
timestamp, ok := math.ParseBig(genesis.Timestamp) timestamp, ok := math.ParseBig256(genesis.Timestamp)
if !ok { if !ok {
return nil, fmt.Errorf("invalid timestamp: %q", genesis.Timestamp) return nil, fmt.Errorf("invalid timestamp: %q", genesis.Timestamp)
} }

View file

@ -171,8 +171,8 @@ func runStateTest(chainConfig *params.ChainConfig, test VmTest) error {
return fmt.Errorf("did not find expected post-state account: %s", addr) return fmt.Errorf("did not find expected post-state account: %s", addr)
} }
if balance := statedb.GetBalance(address); balance.Cmp(math.MustParseBig(account.Balance)) != 0 { if balance := statedb.GetBalance(address); balance.Cmp(math.MustParseBig256(account.Balance)) != 0 {
return fmt.Errorf("(%x) balance failed. Expected: %v have: %v\n", address[:4], math.MustParseBig(account.Balance), balance) return fmt.Errorf("(%x) balance failed. Expected: %v have: %v\n", address[:4], math.MustParseBig256(account.Balance), balance)
} }
if nonce := statedb.GetNonce(address); nonce != math.MustParseUint64(account.Nonce) { if nonce := statedb.GetNonce(address); nonce != math.MustParseUint64(account.Nonce) {
@ -206,7 +206,7 @@ func runStateTest(chainConfig *params.ChainConfig, test VmTest) error {
func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx map[string]string) ([]byte, []*types.Log, *big.Int, error) { func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx map[string]string) ([]byte, []*types.Log, *big.Int, error) {
environment, msg := NewEVMEnvironment(false, chainConfig, statedb, env, tx) environment, msg := NewEVMEnvironment(false, chainConfig, statedb, env, tx)
gaspool := new(core.GasPool).AddGas(math.MustParseBig(env["currentGasLimit"])) gaspool := new(core.GasPool).AddGas(math.MustParseBig256(env["currentGasLimit"]))
root, _ := statedb.Commit(false) root, _ := statedb.Commit(false)
statedb.Reset(root) statedb.Reset(root)

View file

@ -162,7 +162,7 @@ func verifyTxFields(chainConfig *params.ChainConfig, txTest TransactionTest, dec
var decodedSender common.Address var decodedSender common.Address
signer := types.MakeSigner(chainConfig, math.MustParseBig(txTest.Blocknumber)) signer := types.MakeSigner(chainConfig, math.MustParseBig256(txTest.Blocknumber))
decodedSender, err = types.Sender(signer, decodedTx) decodedSender, err = types.Sender(signer, decodedTx)
if err != nil { if err != nil {
return err return err

View file

@ -121,7 +121,7 @@ func insertAccount(state *state.StateDB, saddr string, account Account) {
addr := common.HexToAddress(saddr) addr := common.HexToAddress(saddr)
state.SetCode(addr, common.Hex2Bytes(account.Code)) state.SetCode(addr, common.Hex2Bytes(account.Code))
state.SetNonce(addr, math.MustParseUint64(account.Nonce)) state.SetNonce(addr, math.MustParseUint64(account.Nonce))
state.SetBalance(addr, math.MustParseBig(account.Balance)) state.SetBalance(addr, math.MustParseBig256(account.Balance))
for a, v := range account.Storage { for a, v := range account.Storage {
state.SetState(addr, common.HexToHash(a), common.HexToHash(v)) state.SetState(addr, common.HexToHash(a), common.HexToHash(v))
} }
@ -153,9 +153,9 @@ type VmTest struct {
func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *state.StateDB, envValues map[string]string, tx map[string]string) (*vm.EVM, core.Message) { func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *state.StateDB, envValues map[string]string, tx map[string]string) (*vm.EVM, core.Message) {
var ( var (
data = common.FromHex(tx["data"]) data = common.FromHex(tx["data"])
gas = math.MustParseBig(tx["gasLimit"]) gas = math.MustParseBig256(tx["gasLimit"])
price = math.MustParseBig(tx["gasPrice"]) price = math.MustParseBig256(tx["gasPrice"])
value = math.MustParseBig(tx["value"]) value = math.MustParseBig256(tx["value"])
nonce = math.MustParseUint64(tx["nonce"]) nonce = math.MustParseUint64(tx["nonce"])
) )
@ -199,10 +199,10 @@ func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *st
Origin: origin, Origin: origin,
Coinbase: common.HexToAddress(envValues["currentCoinbase"]), Coinbase: common.HexToAddress(envValues["currentCoinbase"]),
BlockNumber: math.MustParseBig(envValues["currentNumber"]), BlockNumber: math.MustParseBig256(envValues["currentNumber"]),
Time: math.MustParseBig(envValues["currentTimestamp"]), Time: math.MustParseBig256(envValues["currentTimestamp"]),
GasLimit: math.MustParseBig(envValues["currentGasLimit"]), GasLimit: math.MustParseBig256(envValues["currentGasLimit"]),
Difficulty: math.MustParseBig(envValues["currentDifficulty"]), Difficulty: math.MustParseBig256(envValues["currentDifficulty"]),
GasPrice: price, GasPrice: price,
} }
if context.GasPrice == nil { if context.GasPrice == nil {

View file

@ -181,7 +181,7 @@ func runVmTest(test VmTest) error {
if len(test.Gas) == 0 && err == nil { if len(test.Gas) == 0 && err == nil {
return fmt.Errorf("gas unspecified, indicating an error. VM returned (incorrectly) successful") return fmt.Errorf("gas unspecified, indicating an error. VM returned (incorrectly) successful")
} else { } else {
gexp := math.MustParseBig(test.Gas) gexp := math.MustParseBig256(test.Gas)
if gexp.Cmp(gas) != 0 { if gexp.Cmp(gas) != 0 {
return fmt.Errorf("gas failed. Expected %v, got %v\n", gexp, gas) return fmt.Errorf("gas failed. Expected %v, got %v\n", gexp, gas)
} }
@ -223,8 +223,8 @@ func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, []*type
to = common.HexToAddress(exec["address"]) to = common.HexToAddress(exec["address"])
from = common.HexToAddress(exec["caller"]) from = common.HexToAddress(exec["caller"])
data = common.FromHex(exec["data"]) data = common.FromHex(exec["data"])
gas = math.MustParseBig(exec["gas"]) gas = math.MustParseBig256(exec["gas"])
value = math.MustParseBig(exec["value"]) value = math.MustParseBig256(exec["value"])
) )
caller := statedb.GetOrNewStateObject(from) caller := statedb.GetOrNewStateObject(from)
vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract) vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)