From bcec04e784830d45797201cb889246bf266cd941 Mon Sep 17 00:00:00 2001 From: czechbol Date: Fri, 23 Aug 2024 17:17:23 +0200 Subject: [PATCH] Fix conversion overflow false positives when they are checked or pre-determined Signed-off-by: czechbol --- analyzers/conversion_overflow.go | 91 ++++++++++++++++++++++++++++++++ testutils/g115_samples.go | 91 ++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) diff --git a/analyzers/conversion_overflow.go b/analyzers/conversion_overflow.go index c5d6a59..540964a 100644 --- a/analyzers/conversion_overflow.go +++ b/analyzers/conversion_overflow.go @@ -16,6 +16,7 @@ package analyzers import ( "fmt" + "go/token" "regexp" "strconv" @@ -49,6 +50,9 @@ func runConversionOverflow(pass *analysis.Pass) (interface{}, error) { case *ssa.Convert: src := instr.X.Type().Underlying().String() dst := instr.Type().Underlying().String() + if isSafeConversion(instr) { + continue + } if isIntOverflow(src, dst) { issue := newIssue(pass.Analyzer.Name, fmt.Sprintf("integer overflow conversion %s -> %s", src, dst), @@ -70,6 +74,93 @@ func runConversionOverflow(pass *analysis.Pass) (interface{}, error) { return nil, nil } +func isSafeConversion(instr *ssa.Convert) bool { + dstType := instr.Type().Underlying().String() + + // Check for constant conversions + if constVal, ok := instr.X.(*ssa.Const); ok { + if isConstantInRange(constVal, dstType) { + return true + } + } + + // Check for explicit range checks + if hasExplicitRangeCheck(instr) { + return true + } + + // Check for string to integer conversions with specified bit size + if isStringToIntConversion(instr, dstType) { + return true + } + + return false +} + +func isConstantInRange(constVal *ssa.Const, dstType string) bool { + value, err := strconv.ParseInt(constVal.Value.String(), 10, 64) + if err != nil { + return false + } + + dstInt, err := parseIntType(dstType) + if err != nil { + return false + } + + if dstInt.signed { + return value >= -(1<<(dstInt.size-1)) && value <= (1<<(dstInt.size-1))-1 + } + return value >= 0 && value <= (1< math.MaxInt32 { + panic("out of range") + } + b := int32(a) + fmt.Printf("%d\n", b) +} + `, + }, 0, gosec.NewConfig()}, + {[]string{ + ` +package main + +import ( + "fmt" + "math" + "math/rand" +) + +func main() { + a := rand.Int63() + if a < math.MinInt64 || a > math.MaxInt32 { + panic("out of range") + } + b := int32(a) + fmt.Printf("%d\n", b) +} + `, + }, 1, gosec.NewConfig()}, + {[]string{ + ` +package main + +import ( + "fmt" + "math" +) + +func main() { + var a int32 = math.MaxInt32 + if a < math.MinInt32 || a > math.MaxInt32 { + panic("out of range") + } + var b int64 = int64(a) * 2 + c := int32(b) + fmt.Printf("%d\n", c) +} + `, + }, 1, gosec.NewConfig()}, + {[]string{ + ` +package main + +import ( + "fmt" + "strconv" +) + +func main() { + var a string = "13" + b, _ := strconv.ParseInt(a, 10, 32) + c := int32(b) + fmt.Printf("%d\n", c) } `, }, 0, gosec.NewConfig()},