1
1
/**
2
2
* @name Incorrect conversion between integer types
3
- * @description Converting the result of strconv.Atoi, strconv.ParseInt and strconv.ParseUint
4
- * to integer types of smaller bit size can produce unexpected values.
3
+ * @description Converting the result of `strconv.Atoi`, `strconv.ParseInt`,
4
+ * and `strconv.ParseUint` to integer types of smaller bit size
5
+ * can produce unexpected values.
5
6
* @kind path-problem
6
7
* @problem.severity warning
7
8
* @id go/incorrect-integer-conversion
@@ -19,7 +20,7 @@ import DataFlow::PathGraph
19
20
* is true, unsigned otherwise) with `bitSize` bits.
20
21
*/
21
22
float getMaxIntValue ( int bitSize , boolean isSigned ) {
22
- bitSize in [ 8 , 16 , 32 , 64 ] and
23
+ bitSize in [ 8 , 16 , 32 ] and
23
24
(
24
25
isSigned = true and result = 2 .pow ( bitSize - 1 ) - 1
25
26
or
@@ -33,15 +34,15 @@ float getMaxIntValue(int bitSize, boolean isSigned) {
33
34
* architecture-dependent.
34
35
*/
35
36
private predicate isIncorrectIntegerConversion ( int sourceBitSize , int sinkBitSize ) {
36
- sourceBitSize in [ 0 , 16 , 32 , 64 ] and
37
- sinkBitSize in [ 0 , 8 , 16 , 32 ] and
38
- not ( sourceBitSize = 0 and sinkBitSize = 0 ) and
39
- exists ( int source , int sink |
40
- ( if sourceBitSize = 0 then source = 64 else source = sourceBitSize ) and
41
- if sinkBitSize = 0 then sink = 32 else sink = sinkBitSize
42
- |
43
- source > sink
44
- )
37
+ sourceBitSize in [ 16 , 32 , 64 ] and
38
+ sinkBitSize in [ 8 , 16 , 32 ] and
39
+ sourceBitSize > sinkBitSize
40
+ or
41
+ sourceBitSize = 0 and
42
+ sinkBitSize in [ 8 , 16 , 32 ]
43
+ or
44
+ sourceBitSize = 64 and
45
+ sinkBitSize = 0
45
46
}
46
47
47
48
/**
@@ -57,15 +58,24 @@ class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
57
58
ConversionWithoutBoundsCheckConfig ( ) {
58
59
sourceIsSigned in [ true , false ] and
59
60
isIncorrectIntegerConversion ( sourceBitSize , sinkBitSize ) and
60
- this =
61
- sourceBitSize .toString ( ) + sourceIsSigned .toString ( ) + sinkBitSize .toString ( ) +
62
- "ConversionWithoutBoundsCheckConfig"
61
+ this = "ConversionWithoutBoundsCheckConfig" + sourceBitSize + sourceIsSigned + sinkBitSize
63
62
}
64
63
64
+ int getSourceBitSize ( ) { result = sourceBitSize }
65
+
65
66
override predicate isSource ( DataFlow:: Node source ) {
66
- exists ( ParserCall pc , int bitSize | source = pc .getResult ( 0 ) |
67
- ( if pc .targetIsSigned ( ) then sourceIsSigned = true else sourceIsSigned = false ) and
68
- ( if pc .getTargetBitSize ( ) = 0 then bitSize = 0 else bitSize = pc .getTargetBitSize ( ) ) and
67
+ exists ( DataFlow:: CallNode c , IntegerParser:: Range ip , int bitSize |
68
+ c .getTarget ( ) = ip and source = c .getResult ( 0 )
69
+ |
70
+ (
71
+ if ip .getResultType ( 0 ) instanceof SignedIntegerType
72
+ then sourceIsSigned = true
73
+ else sourceIsSigned = false
74
+ ) and
75
+ (
76
+ bitSize = ip .getTargetBitSize ( ) or
77
+ bitSize = ip .getTargetBitSizeInput ( ) .getNode ( c ) .getIntValue ( )
78
+ ) and
69
79
// `bitSize` could be any value between 0 and 64, but we can round
70
80
// it up to the nearest size of an integer type without changing
71
81
// behaviour.
@@ -76,16 +86,14 @@ class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
76
86
/**
77
87
* Holds if `sink` is a typecast to an integer type with size `bitSize` (where
78
88
* 0 represents architecture-dependent) and the expression being typecast is
79
- * not also in a right-shift expression.
89
+ * not also in a right-shift expression. We allow this case because it is
90
+ * a common pattern to serialise `byte(v)`, `byte(v >> 8)`, and so on.
80
91
*/
81
92
predicate isSink ( DataFlow:: TypeCastNode sink , int bitSize ) {
82
93
exists ( IntegerType integerType | sink .getType ( ) .getUnderlyingType ( ) = integerType |
83
94
bitSize = integerType .getSize ( )
84
95
or
85
- (
86
- integerType instanceof IntType or
87
- integerType instanceof UintType
88
- ) and
96
+ not exists ( integerType .getSize ( ) ) and
89
97
bitSize = 0
90
98
) and
91
99
not exists ( ShrExpr shrExpr |
@@ -131,12 +139,18 @@ class UpperBoundCheckGuard extends DataFlow::BarrierGuard, DataFlow::RelationalC
131
139
}
132
140
}
133
141
142
+ /** Gets a string describing the size of the integer parsed. */
143
+ string describeBitSize ( int bitSize ) {
144
+ if bitSize != 0
145
+ then bitSize in [ 8 , 16 , 32 , 64 ] and result = "a " + bitSize + "-bit integer"
146
+ else result = "an integer with architecture-dependent bit size"
147
+ }
148
+
134
149
from
135
150
DataFlow:: PathNode source , DataFlow:: PathNode sink , ConversionWithoutBoundsCheckConfig cfg ,
136
- ParserCall pc
137
- where cfg .hasFlowPath ( source , sink ) and pc .getResult ( 0 ) = source .getNode ( )
151
+ DataFlow :: CallNode call
152
+ where cfg .hasFlowPath ( source , sink ) and call .getResult ( 0 ) = source .getNode ( )
138
153
select source .getNode ( ) , source , sink ,
139
- "Incorrect conversion of " + pc .getBitSizeString ( ) + " from " + pc .getParserName ( ) +
140
- " to a lower bit size type " +
141
- sink .getNode ( ) .( DataFlow:: TypeCastNode ) .getType ( ) .getUnderlyingType ( ) .getName ( ) +
142
- " without an upper bound check."
154
+ "Incorrect conversion of " + describeBitSize ( cfg .getSourceBitSize ( ) ) + " from " +
155
+ call .getTarget ( ) .getQualifiedName ( ) + " to a lower bit size type " +
156
+ sink .getNode ( ) .getType ( ) .getUnderlyingType ( ) .getName ( ) + " without an upper bound check."
0 commit comments