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

View file

@ -28,23 +28,30 @@ var (
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.
func ParseBig(s string) (*big.Int, bool) {
func ParseBig256(s string) (*big.Int, bool) {
if s == "" {
return new(big.Int), true
}
var bigint *big.Int
var ok bool
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.
func MustParseBig(s string) *big.Int {
v, ok := ParseBig(s)
// MustParseBig parses s as a 256 bit big integer and panics if the string is invalid.
func MustParseBig256(s string) *big.Int {
v, ok := ParseBig256(s)
if !ok {
panic("invalid integer: " + s)
panic("invalid 256 bit integer: " + s)
}
return v
}

View file

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

View file

@ -68,7 +68,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
// creating with empty hash always works
statedb, _ := state.New(common.Hash{}, chainDb)
for addr, account := range genesis.Alloc {
balance, ok := math.ParseBig(account.Balance)
balance, ok := math.ParseBig256(account.Balance)
if !ok {
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)
difficulty, ok := math.ParseBig(genesis.Difficulty)
difficulty, ok := math.ParseBig256(genesis.Difficulty)
if !ok {
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 {
return nil, fmt.Errorf("invalid nonce: %q", genesis.Nonce)
}
timestamp, ok := math.ParseBig(genesis.Timestamp)
timestamp, ok := math.ParseBig256(genesis.Timestamp)
if !ok {
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)
}
if balance := statedb.GetBalance(address); balance.Cmp(math.MustParseBig(account.Balance)) != 0 {
return fmt.Errorf("(%x) balance failed. Expected: %v have: %v\n", address[:4], math.MustParseBig(account.Balance), balance)
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.MustParseBig256(account.Balance), balance)
}
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) {
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)
statedb.Reset(root)

View file

@ -162,7 +162,7 @@ func verifyTxFields(chainConfig *params.ChainConfig, txTest TransactionTest, dec
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)
if err != nil {
return err

View file

@ -121,7 +121,7 @@ func insertAccount(state *state.StateDB, saddr string, account Account) {
addr := common.HexToAddress(saddr)
state.SetCode(addr, common.Hex2Bytes(account.Code))
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 {
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) {
var (
data = common.FromHex(tx["data"])
gas = math.MustParseBig(tx["gasLimit"])
price = math.MustParseBig(tx["gasPrice"])
value = math.MustParseBig(tx["value"])
gas = math.MustParseBig256(tx["gasLimit"])
price = math.MustParseBig256(tx["gasPrice"])
value = math.MustParseBig256(tx["value"])
nonce = math.MustParseUint64(tx["nonce"])
)
@ -199,10 +199,10 @@ func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *st
Origin: origin,
Coinbase: common.HexToAddress(envValues["currentCoinbase"]),
BlockNumber: math.MustParseBig(envValues["currentNumber"]),
Time: math.MustParseBig(envValues["currentTimestamp"]),
GasLimit: math.MustParseBig(envValues["currentGasLimit"]),
Difficulty: math.MustParseBig(envValues["currentDifficulty"]),
BlockNumber: math.MustParseBig256(envValues["currentNumber"]),
Time: math.MustParseBig256(envValues["currentTimestamp"]),
GasLimit: math.MustParseBig256(envValues["currentGasLimit"]),
Difficulty: math.MustParseBig256(envValues["currentDifficulty"]),
GasPrice: price,
}
if context.GasPrice == nil {

View file

@ -181,7 +181,7 @@ func runVmTest(test VmTest) error {
if len(test.Gas) == 0 && err == nil {
return fmt.Errorf("gas unspecified, indicating an error. VM returned (incorrectly) successful")
} else {
gexp := math.MustParseBig(test.Gas)
gexp := math.MustParseBig256(test.Gas)
if gexp.Cmp(gas) != 0 {
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"])
from = common.HexToAddress(exec["caller"])
data = common.FromHex(exec["data"])
gas = math.MustParseBig(exec["gas"])
value = math.MustParseBig(exec["value"])
gas = math.MustParseBig256(exec["gas"])
value = math.MustParseBig256(exec["value"])
)
caller := statedb.GetOrNewStateObject(from)
vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)