Add static analysis for unsafe uint casting (#10318)

* Add static analysis for unsafe uint casting

* Fix violations of uintcast

* go mod tidy

* Add exclusion to nogo for darwin build

* Add test for math.Int

* Move some things to const so they are assured not to exceed int64

* Self review

* lint

* fix tests

* fix test

* Add init check for non 64 bit OS

* Move new deps from WORKSPACE to deps.bzl

* fix bazel build for go analysis runs

* Update BUILD.bazel

Remove TODO

* add math.AddInt method

* Add new test casts

* Add case where builtin functions and declared functions are covered

* Fix new findings

* cleanup

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: Nishant Das <nishdas93@gmail.com>
This commit is contained in:
Preston Van Loon
2022-03-11 03:34:30 -06:00
committed by GitHub
parent 693cc79cc9
commit c1197d7881
99 changed files with 1081 additions and 220 deletions

View File

@@ -9,6 +9,19 @@ import (
"github.com/thomaso-mirodin/intmath/u64"
)
func init() {
// The Int function assumes that the operating system is 64 bit. In any case, Ethereum
// consensus layer uses 64 bit values almost exclusively so 64 bit OS requirement should
// already be established. This panic is a strict fail fast feedback to alert 32 bit users
// that they are not supported.
if stdmath.MaxUint < stdmath.MaxUint64 {
panic("Prysm is only supported on 64 bit OS")
}
}
// ErrOverflow occurs when an operation exceeds max or minimum values.
var ErrOverflow = errors.New("integer overflow")
// Common square root values.
var squareRootTable = map[uint64]uint64{
4: 2,
@@ -121,3 +134,28 @@ func Sub64(a, b uint64) (uint64, error) {
}
return res, nil
}
// Int returns the integer value of the uint64 argument. If there is an overlow, then an error is
// returned.
func Int(u uint64) (int, error) {
if u > stdmath.MaxInt {
return 0, ErrOverflow
}
return int(u), nil // lint:ignore uintcast -- This is the preferred method of casting uint64 to int.
}
// AddInt adds two or more integers and checks for integer overflows.
func AddInt(i ...int) (int, error) {
var sum int
for _, ii := range i {
if ii > 0 && sum > stdmath.MaxInt-ii {
return 0, ErrOverflow
} else if ii < 0 && sum < stdmath.MinInt-ii {
return 0, ErrOverflow
}
sum += ii
}
return sum, nil
}

View File

@@ -1,6 +1,7 @@
package math_test
import (
"fmt"
stdmath "math"
"testing"
@@ -366,3 +367,87 @@ func TestMath_Sub64(t *testing.T) {
}
}
}
func TestInt(t *testing.T) {
tests := []struct {
arg uint64
want int
wantErr bool
}{
{
arg: 0,
want: 0,
},
{
arg: 10000000,
want: 10000000,
},
{
arg: stdmath.MaxInt64,
want: stdmath.MaxInt64,
},
{
arg: stdmath.MaxInt64 + 1,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(fmt.Sprint(tt.arg), func(t *testing.T) {
got, err := math.Int(tt.arg)
if (err != nil) != tt.wantErr {
t.Errorf("Int() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("Int() = %v, want %v", got, tt.want)
}
})
}
}
func TestAddInt(t *testing.T) {
tests := []struct {
name string
args []int
want int
wantErr bool
}{
{
name: "no overflow",
args: []int{1, 2, 3, 4, 5},
want: 15,
},
{
name: "overflow",
args: []int{1, stdmath.MaxInt},
wantErr: true,
},
{
name: "underflow",
args: []int{-1, stdmath.MinInt},
wantErr: true,
},
{
name: "max int",
args: []int{1, stdmath.MaxInt - 1},
want: stdmath.MaxInt,
},
{
name: "min int",
args: []int{-1, stdmath.MinInt + 1},
want: stdmath.MinInt,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := math.AddInt(tt.args...)
if (err != nil) != tt.wantErr {
t.Errorf("AddInt() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("AddInt() = %v, want %v", got, tt.want)
}
})
}
}