Skip to content

Commit

Permalink
basic equal operator logic
Browse files Browse the repository at this point in the history
Signed-off-by: czechbol <adamludes@gmail.com>
  • Loading branch information
czechbol committed Aug 30, 2024
1 parent 8b91515 commit ade882a
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 18 deletions.
88 changes: 70 additions & 18 deletions analyzers/conversion_overflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"math"
"regexp"
"strconv"
"strings"

"golang.org/x/exp/constraints"
"golang.org/x/tools/go/analysis"
Expand All @@ -37,16 +38,20 @@ type integer struct {
}

type rangeResult struct {
MinValue int
MaxValue uint
IsRangeCheck bool
ConvertFound bool
MinValue int
MaxValue uint
ExplixitPositiveVals []uint
ExplicitNegativeVals []int
IsRangeCheck bool
ConvertFound bool
}

type branchBounds struct {
MinValue *int
MaxValue *uint
ConvertFound bool
type branchResults struct {
MinValue *int
MaxValue *uint
ExplixitPositiveVals []uint
ExplicitNegativeVals []int
ConvertFound bool
}

func newConversionOverflowAnalyzer(id string, description string) *analysis.Analyzer {
Expand Down Expand Up @@ -151,11 +156,6 @@ func parseIntType(intType string) (integer, error) {
min = -1 << (intSize - 1)

} else {
// Perform a bounds check
if intSize < 0 {
return integer{}, fmt.Errorf("invalid bit size: %d", intSize)
}

max = (1 << uint(intSize)) - 1
min = 0
}
Expand Down Expand Up @@ -255,6 +255,8 @@ func hasExplicitRangeCheck(instr *ssa.Convert, dstType string) bool {

minValue := srcInt.min
maxValue := srcInt.max
explicitPositiveVals := []uint{}
explicitNegativeVals := []int{}

if minValue > dstInt.min && maxValue < dstInt.max {
return true
Expand All @@ -269,6 +271,8 @@ func hasExplicitRangeCheck(instr *ssa.Convert, dstType string) bool {
if result.IsRangeCheck {
minValue = max(minValue, &result.MinValue)
maxValue = min(maxValue, &result.MaxValue)
explicitPositiveVals = append(explicitPositiveVals, result.ExplixitPositiveVals...)
explicitNegativeVals = append(explicitNegativeVals, result.ExplicitNegativeVals...)
}
case *ssa.Call:
// these function return an int of a guaranteed size
Expand All @@ -287,7 +291,9 @@ func hasExplicitRangeCheck(instr *ssa.Convert, dstType string) bool {
}
}

if minValue >= dstInt.min && maxValue <= dstInt.max {
if explicitValsInRange(explicitPositiveVals, explicitNegativeVals, dstInt) {
return true
} else if minValue >= dstInt.min && maxValue <= dstInt.max {
return true
}
}
Expand Down Expand Up @@ -350,6 +356,29 @@ func updateResultFromBinOp(result *rangeResult, binOp *ssa.BinOp, instr *ssa.Con
updateMinMaxForLessOrEqual(result, constVal, binOp.Op, operandsFlipped, successPathConvert)
case token.GEQ, token.GTR:
updateMinMaxForGreaterOrEqual(result, constVal, binOp.Op, operandsFlipped, successPathConvert)
case token.EQL:
if !successPathConvert {
break
}

// determine if the constant value is positive or negative
if strings.Contains(constVal.String(), "-") {
result.ExplicitNegativeVals = append(result.ExplicitNegativeVals, int(constVal.Int64()))
} else {
result.ExplixitPositiveVals = append(result.ExplixitPositiveVals, uint(constVal.Uint64()))
}

case token.NEQ:
if successPathConvert {
break
}

// determine if the constant value is positive or negative
if strings.Contains(constVal.String(), "-") {
result.ExplicitNegativeVals = append(result.ExplicitNegativeVals, int(constVal.Int64()))
} else {
result.ExplixitPositiveVals = append(result.ExplixitPositiveVals, uint(constVal.Uint64()))
}
}
}

Expand Down Expand Up @@ -384,8 +413,8 @@ func updateMinMaxForGreaterOrEqual(result *rangeResult, constVal *ssa.Const, op
}

// walkBranchForConvert walks the branch of the if statement to find the range of the variable and where the conversion is
func walkBranchForConvert(block *ssa.BasicBlock, instr *ssa.Convert, visitedIfs map[*ssa.If]bool) branchBounds {
bounds := branchBounds{}
func walkBranchForConvert(block *ssa.BasicBlock, instr *ssa.Convert, visitedIfs map[*ssa.If]bool) branchResults {
bounds := branchResults{}

for _, blockInstr := range block.Instrs {
switch v := blockInstr.(type) {
Expand Down Expand Up @@ -417,12 +446,35 @@ func walkBranchForConvert(block *ssa.BasicBlock, instr *ssa.Convert, visitedIfs
func isRangeCheck(v ssa.Value, x ssa.Value) bool {
switch op := v.(type) {
case *ssa.BinOp:
return (op.X == x || op.Y == x) &&
(op.Op == token.LSS || op.Op == token.LEQ || op.Op == token.GTR || op.Op == token.GEQ)
switch op.Op {
case token.LSS, token.LEQ, token.GTR, token.GEQ,
token.EQL, token.NEQ:
return op.X == x || op.Y == x
}
}
return false
}

func explicitValsInRange(explicitPosVals []uint, explicitNegVals []int, dstInt integer) bool {
if len(explicitPosVals) == 0 && len(explicitNegVals) == 0 {
return false
}

for _, val := range explicitPosVals {
if val > dstInt.max {
return false
}
}

for _, val := range explicitNegVals {
if val < dstInt.min {
return false
}
}

return true
}

func min[T constraints.Integer](a T, b *T) T {
if b == nil {
return a
Expand Down
38 changes: 38 additions & 0 deletions testutils/g115_samples.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,44 @@ func main() {
fmt.Printf("%d\n", b)
}
panic("out of range")
}
`,
}, 0, gosec.NewConfig()},
{[]string{
`
package main
import (
"fmt"
"math/rand"
)
func main() {
a := rand.Int63()
if a == 3 || a == 4 {
b := int32(a)
fmt.Printf("%d\n", b)
}
panic("out of range")
}
`,
}, 0, gosec.NewConfig()},
{[]string{
`
package main
import (
"fmt"
"math/rand"
)
func main() {
a := rand.Int63()
if a != 3 || a != 4 {
panic("out of range")
}
b := int32(a)
fmt.Printf("%d\n", b)
}
`,
}, 0, gosec.NewConfig()},
Expand Down

0 comments on commit ade882a

Please sign in to comment.