11/**
22 * @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.
56 * @kind path-problem
67 * @problem.severity warning
78 * @id go/incorrect-integer-conversion
@@ -19,7 +20,7 @@ import DataFlow::PathGraph
1920 * is true, unsigned otherwise) with `bitSize` bits.
2021 */
2122float getMaxIntValue ( int bitSize , boolean isSigned ) {
22- bitSize in [ 8 , 16 , 32 , 64 ] and
23+ bitSize in [ 8 , 16 , 32 ] and
2324 (
2425 isSigned = true and result = 2 .pow ( bitSize - 1 ) - 1
2526 or
@@ -33,15 +34,15 @@ float getMaxIntValue(int bitSize, boolean isSigned) {
3334 * architecture-dependent.
3435 */
3536private 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
4546}
4647
4748/**
@@ -57,15 +58,24 @@ class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
5758 ConversionWithoutBoundsCheckConfig ( ) {
5859 sourceIsSigned in [ true , false ] and
5960 isIncorrectIntegerConversion ( sourceBitSize , sinkBitSize ) and
60- this =
61- sourceBitSize .toString ( ) + sourceIsSigned .toString ( ) + sinkBitSize .toString ( ) +
62- "ConversionWithoutBoundsCheckConfig"
61+ this = "ConversionWithoutBoundsCheckConfig" + sourceBitSize + sourceIsSigned + sinkBitSize
6362 }
6463
64+ int getSourceBitSize ( ) { result = sourceBitSize }
65+
6566 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
6979 // `bitSize` could be any value between 0 and 64, but we can round
7080 // it up to the nearest size of an integer type without changing
7181 // behaviour.
@@ -76,16 +86,14 @@ class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
7686 /**
7787 * Holds if `sink` is a typecast to an integer type with size `bitSize` (where
7888 * 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.
8091 */
8192 predicate isSink ( DataFlow:: TypeCastNode sink , int bitSize ) {
8293 exists ( IntegerType integerType | sink .getType ( ) .getUnderlyingType ( ) = integerType |
8394 bitSize = integerType .getSize ( )
8495 or
85- (
86- integerType instanceof IntType or
87- integerType instanceof UintType
88- ) and
96+ not exists ( integerType .getSize ( ) ) and
8997 bitSize = 0
9098 ) and
9199 not exists ( ShrExpr shrExpr |
@@ -131,12 +139,18 @@ class UpperBoundCheckGuard extends DataFlow::BarrierGuard, DataFlow::RelationalC
131139 }
132140}
133141
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+
134149from
135150 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 ( )
138153select 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