@@ -16,6 +16,7 @@ package analyzers
16
16
17
17
import (
18
18
"fmt"
19
+ "go/token"
19
20
"regexp"
20
21
"strconv"
21
22
@@ -49,6 +50,9 @@ func runConversionOverflow(pass *analysis.Pass) (interface{}, error) {
49
50
case * ssa.Convert :
50
51
src := instr .X .Type ().Underlying ().String ()
51
52
dst := instr .Type ().Underlying ().String ()
53
+ if isSafeConversion (instr ) {
54
+ continue
55
+ }
52
56
if isIntOverflow (src , dst ) {
53
57
issue := newIssue (pass .Analyzer .Name ,
54
58
fmt .Sprintf ("integer overflow conversion %s -> %s" , src , dst ),
@@ -70,6 +74,93 @@ func runConversionOverflow(pass *analysis.Pass) (interface{}, error) {
70
74
return nil , nil
71
75
}
72
76
77
+ func isSafeConversion (instr * ssa.Convert ) bool {
78
+ dstType := instr .Type ().Underlying ().String ()
79
+
80
+ // Check for constant conversions
81
+ if constVal , ok := instr .X .(* ssa.Const ); ok {
82
+ if isConstantInRange (constVal , dstType ) {
83
+ return true
84
+ }
85
+ }
86
+
87
+ // Check for explicit range checks
88
+ if hasExplicitRangeCheck (instr ) {
89
+ return true
90
+ }
91
+
92
+ // Check for string to integer conversions with specified bit size
93
+ if isStringToIntConversion (instr , dstType ) {
94
+ return true
95
+ }
96
+
97
+ return false
98
+ }
99
+
100
+ func isConstantInRange (constVal * ssa.Const , dstType string ) bool {
101
+ value , err := strconv .ParseInt (constVal .Value .String (), 10 , 64 )
102
+ if err != nil {
103
+ return false
104
+ }
105
+
106
+ dstInt , err := parseIntType (dstType )
107
+ if err != nil {
108
+ return false
109
+ }
110
+
111
+ if dstInt .signed {
112
+ return value >= - (1 << (dstInt .size - 1 )) && value <= (1 << (dstInt .size - 1 ))- 1
113
+ }
114
+ return value >= 0 && value <= (1 << dstInt .size )- 1
115
+ }
116
+
117
+ func hasExplicitRangeCheck (instr * ssa.Convert ) bool {
118
+ block := instr .Block ()
119
+ for _ , i := range block .Instrs {
120
+ if binOp , ok := i .(* ssa.BinOp ); ok {
121
+ // Check if either operand of the BinOp is the result of the Convert instruction
122
+ if (binOp .X == instr || binOp .Y == instr ) &&
123
+ (binOp .Op == token .LSS || binOp .Op == token .LEQ || binOp .Op == token .GTR || binOp .Op == token .GEQ ) {
124
+ return true
125
+ }
126
+ }
127
+ }
128
+ return false
129
+ }
130
+
131
+ func isStringToIntConversion (instr * ssa.Convert , dstType string ) bool {
132
+ // Traverse the SSA instructions to find the original variable
133
+ original := instr .X
134
+ for {
135
+ switch v := original .(type ) {
136
+ case * ssa.Call :
137
+ if v .Call .StaticCallee () != nil && v .Call .StaticCallee ().Name () == "ParseInt" {
138
+ if len (v .Call .Args ) == 3 {
139
+ if bitSize , ok := v .Call .Args [2 ].(* ssa.Const ); ok {
140
+ bitSizeValue , err := strconv .Atoi (bitSize .Value .String ())
141
+ if err != nil {
142
+ return false
143
+ }
144
+ dstInt , err := parseIntType (dstType )
145
+ if err != nil {
146
+ return false
147
+ }
148
+ isSafe := bitSizeValue <= dstInt .size
149
+ return isSafe
150
+ }
151
+ }
152
+ }
153
+ return false
154
+ case * ssa.Phi :
155
+ original = v .Edges [0 ]
156
+ case * ssa.Extract :
157
+ original = v .Tuple
158
+ default :
159
+ return false
160
+ }
161
+ }
162
+ }
163
+
73
164
type integer struct {
74
165
signed bool
75
166
size int
@@ -100,7 +191,10 @@ func parseIntType(intType string) (integer, error) {
100
191
}
101
192
}
102
193
103
- return integer {signed : signed , size : intSize }, nil
194
+ return integer {
195
+ signed : signed ,
196
+ size : intSize ,
197
+ }, nil
104
198
}
105
199
106
200
func isIntOverflow (src string , dst string ) bool {
0 commit comments