@@ -153,13 +153,13 @@ deprecated class ConversionWithoutBoundsCheckConfig extends TaintTracking::Confi
153153
154154private int validBitSize ( ) { result = [ 7 , 8 , 15 , 16 , 31 , 32 , 63 , 64 ] }
155155
156- private newtype TarchitectureBitSize =
156+ private newtype TArchitectureBitSize =
157157 TMk32Bit ( ) or
158158 TMk64Bit ( ) or
159159 TMkArchitectureIndependent ( )
160160
161161private newtype TMaxValueState =
162- TMkMaxValueState ( int bitSize , TarchitectureBitSize architectureBitSize ) {
162+ TMkMaxValueState ( int bitSize , TArchitectureBitSize architectureBitSize ) {
163163 bitSize = validBitSize ( )
164164 }
165165
@@ -168,6 +168,11 @@ private class MaxValueState extends TMaxValueState {
168168 /**
169169 * Gets the smallest bitsize where the maximum value that could get to this
170170 * point fits into an integer type whose maximum value is 2^(result) - 1.
171+ *
172+ * For example, if we know `1 << 12` can get to a particular program point,
173+ * then the result would be 15, since a 16-bit signed integer can represent
174+ * that value and that type has maximum value 2^15 -1. An unsigned 8-bit
175+ * integer cannot represent that value as its maximum value is 2^8 - 1.
171176 */
172177 int getBitSize ( ) { this = TMkMaxValueState ( result , _) }
173178
@@ -185,7 +190,8 @@ private class MaxValueState extends TMaxValueState {
185190 * Gets the bitsize we should use for a sink.
186191 *
187192 * If the architecture bit size is known, then we should use that. Otherwise,
188- * we should use 32 bits, because that will lead to more results.
193+ * we should use 32 bits, because that will find results that only exist on
194+ * 32-bit architectures.
189195 */
190196 bindingset [ default]
191197 int getSinkBitSize ( int default ) {
@@ -243,6 +249,10 @@ class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
243249 * Holds if the upper bound check ensures the non-constant operand is less
244250 * than or equal to the maximum value for `bitSize` and `isSigned`. In this
245251 * case, the upper bound check is a barrier guard.
252+ *
253+ * Note that we have to use floats here because integers in CodeQL are
254+ * represented by 32-bit signed integers, which cannot represent some of the
255+ * integer values which we will encounter.
246256 */
247257 deprecated predicate isBoundFor ( int bitSize , boolean isSigned ) {
248258 bitSize = [ 8 , 16 , 32 ] and
@@ -271,7 +281,12 @@ class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
271281 /**
272282 * Holds if this upper bound check ensures the non-constant operand is less
273283 * than or equal to `2^(bitsize) - 1`. In this case, the upper bound check
274- * is a barrier guard.
284+ * is a barrier guard. `architectureBitSize` is used if the constant operand
285+ * is `math.MaxInt` or `math.MaxUint`.
286+ *
287+ * Note that we have to use floats here because integers in CodeQL are
288+ * represented by 32-bit signed integers, which cannot represent some of the
289+ * integer values which we will encounter.
275290 */
276291 predicate isBoundFor2 ( int bitSize , int architectureBitSize ) {
277292 bitSize = validBitSize ( ) and
@@ -311,6 +326,28 @@ class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
311326 * When this guarantees that a variable in the non-constant operand is less
312327 * than some value this may be a barrier guard which should block some flow
313328 * states and transform some others as they flow through.
329+ *
330+ * For example, in the following code:
331+ * ```go
332+ * if parsed <= math.MaxInt16 {
333+ * _ = uint16(parsed)
334+ * }
335+ * ```
336+ * `parsed < math.MaxInt16` is an `UpperBoundCheckGuard` and `uint16(parsed)`
337+ * is an `UpperBoundCheck` that would be a barrier for flow states with bit
338+ * size greater than 15 and would transform them to a flow state with bit size
339+ * 15 and the same architecture bit size.
340+ *
341+ * However, in the following code:
342+ * ```go
343+ * parsed, _ := strconv.ParseUint(input, 10, 32)
344+ * if parsed < 5 {
345+ * _ = uint16(parsed)
346+ * }
347+ * ```
348+ * `parsed < 5` is an `UpperBoundCheckGuard` and `uint16(parsed)` is a barrier
349+ * for all flow states and would not transform any flow states, thus
350+ * effectively blocking them.
314351 */
315352class UpperBoundCheck extends BarrierFlowStateTransformer {
316353 UpperBoundCheckGuard g ;
@@ -320,6 +357,8 @@ class UpperBoundCheck extends BarrierFlowStateTransformer {
320357 }
321358
322359 override predicate barrierFor ( MaxValueState flowstate ) {
360+ // Use a default value of 32 for `MaxValueState.getSinkBitSize` because
361+ // this will find results that only exist on 32-bit architectures.
323362 g .isBoundFor2 ( flowstate .getBitSize ( ) , flowstate .getSinkBitSize ( 32 ) )
324363 }
325364
@@ -329,9 +368,9 @@ class UpperBoundCheck extends BarrierFlowStateTransformer {
329368 max ( int bitsize |
330369 bitsize = validBitSize ( ) and
331370 bitsize < state .getBitSize ( ) and
371+ // Use a default value of 32 for `MaxValueState.getSinkBitSize` because
372+ // this will find results that only exist on 32-bit architectures.
332373 not g .isBoundFor2 ( bitsize , state .getSinkBitSize ( 32 ) )
333- |
334- bitsize
335374 ) and
336375 if exists ( state .getArchitectureBitSize ( ) )
337376 then result .getArchitectureBitSize ( ) = state .getArchitectureBitSize ( )
@@ -341,10 +380,10 @@ class UpperBoundCheck extends BarrierFlowStateTransformer {
341380
342381/**
343382 * Holds if `source` is the result of a call to `strconv.Atoi`,
344- * `strconv.ParseInt`, or `strconv.ParseUint`, `bitSize` is the bit size of
345- * the smallest integer type which the result could be converted to without
346- * data loss , and `isSigned` is true if the result is parsed as a signed
347- * integer .
383+ * `strconv.ParseInt`, or `strconv.ParseUint`, `bitSize` is the `bitSize`
384+ * argument to that call (or 0 for `strconv.Atoi`) and hence must be between 0
385+ * and 64 , and `isSigned` is true for `strconv.Atoi`, true for
386+ * `strconv.ParseInt` and false for `strconv.ParseUint` .
348387 */
349388predicate isSourceWithBitSize ( DataFlow:: Node source , int bitSize , boolean isSigned ) {
350389 exists ( DataFlow:: CallNode c , IntegerParser:: Range ip , int apparentBitSize |
@@ -362,9 +401,8 @@ predicate isSourceWithBitSize(DataFlow::Node source, int bitSize, boolean isSign
362401 else apparentBitSize = rawBitSize .getIntValue ( )
363402 )
364403 ) and
365- // Note that `getIntTypeBitSize` can return 0, so `effectiveBitSize`
366- // can be 0. Also `effectiveBitSize` is not necessarily the bit-size
367- // of an integer type - it can be any integer between 0 and 64.
404+ // Note that `bitSize` is not necessarily the bit-size of an integer type.
405+ // It can be any integer between 0 and 64.
368406 bitSize = replaceZeroWith ( apparentBitSize , getIntTypeBitSize ( source .getFile ( ) , 0 ) ) and
369407 isSigned = ip .isSigned ( )
370408 )
@@ -387,9 +425,9 @@ private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConf
387425 state .getBitSize ( ) =
388426 min ( int bitsize |
389427 bitsize = validBitSize ( ) and
428+ // The `bitSizeForZero` argument will not be used because on this
429+ // branch `effectiveBitSize != 0`.
390430 adjustBitSize ( effectiveBitSize , sourceIsSigned , 64 ) <= bitsize
391- |
392- bitsize
393431 )
394432 )
395433 )
@@ -404,6 +442,8 @@ private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConf
404442 additional predicate isSink2 ( DataFlow:: TypeCastNode sink , FlowState state ) {
405443 sink .asExpr ( ) instanceof ConversionExpr and
406444 exists ( int architectureBitSize , IntegerType integerType , int sinkBitsize , boolean sinkIsSigned |
445+ // Use a default value of 32 for `MaxValueState.getSinkBitSize` because
446+ // this will find results that only exist on 32-bit architectures.
407447 architectureBitSize = getIntTypeBitSize ( sink .getFile ( ) , state .getSinkBitSize ( 32 ) ) and
408448 not ( state .getArchitectureBitSize ( ) = 32 and architectureBitSize = 64 ) and
409449 sink .getResultType ( ) .getUnderlyingType ( ) = integerType and
@@ -438,9 +478,7 @@ private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConf
438478
439479 predicate isBarrier ( DataFlow:: Node node , FlowState state ) {
440480 // Safely guarded by a barrier guard.
441- exists ( BarrierFlowStateTransformer bfst | node = bfst and bfst .barrierFor ( state ) |
442- not exists ( bfst .transform ( state ) ) or bfst .transform ( state ) != state
443- )
481+ exists ( BarrierFlowStateTransformer bfst | node = bfst and bfst .barrierFor ( state ) )
444482 or
445483 // When there is a flow from a source to a sink, do not allow the flow to
446484 // continue to a further sink.
0 commit comments