@@ -289,17 +289,27 @@ private predicate upperBoundCheckGuard(DataFlow::Node g, Expr e, boolean branch)
289
289
/** An upper bound check that compares a variable to a constant value. */
290
290
class UpperBoundCheckGuard extends DataFlow:: RelationalComparisonNode {
291
291
UpperBoundCheckGuard ( ) {
292
+ // Note that even though `x > c` and `x >= c` do not look like upper bound
293
+ // checks, on the branches where they are false the conditions are `x <= c`
294
+ // and `x < c` respectively, which are upper bound checks.
292
295
count ( expr .getAnOperand ( ) .getExactValue ( ) ) = 1 and
293
296
expr .getAnOperand ( ) .getType ( ) .getUnderlyingType ( ) instanceof IntegerType
294
297
}
295
298
296
299
/**
297
- * Holds if this upper bound check ensures the non-constant operand is less
298
- * than or equal to `2^(b) - 1` for some `b` which is a valid bit size less
299
- * than `bitSize`. In this case, the upper bound check is a barrier guard,
300
- * because the flow should be for `b` instead of `bitSize`.
301
- * `architectureBitSize` is used if the constant operand is `math.MaxInt` or
302
- * `math.MaxUint`.
300
+ * Holds if this upper bound check should stop flow for a flow state with bit
301
+ * size `bitSize` and architecture bit size `architectureBitSize`.
302
+ *
303
+ * A flow state has bit size `bitSize` if that is the smallest valid bit size
304
+ * `b` such that the maximum value that could get to that point is less than
305
+ * or equal to `2^(b) - 1`. So the flow should be stopped if there is a valid
306
+ * bit size `b` which is less than `bitSize` such that the maximum value that
307
+ * could get to that point is than or equal to `2^(b) - 1`. In this case,
308
+ * the upper bound check is a barrier guard, because the flow should have bit
309
+ * size equal to the smallest such `b` instead of `bitSize`.
310
+ *
311
+ * The argument `architectureBitSize` is only used if the constant operand is
312
+ * `math.MaxInt` or `math.MaxUint`.
303
313
*
304
314
* Note that we have to use floats here because integers in CodeQL are
305
315
* represented by 32-bit signed integers, which cannot represent some of the
@@ -309,10 +319,12 @@ class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
309
319
bitSize = validBitSize ( ) and
310
320
architectureBitSize = [ 32 , 64 ] and
311
321
exists ( int b , float strictnessOffset |
322
+ // It is sufficient to check for the next valid bit size below `bitSize`.
312
323
b = max ( int a | a = validBitSize ( ) and a < bitSize ) and
313
- // For `x < c` the bound is `c-1`. For `x >= c` we will be an upper bound
314
- // on the `branch` argument of `checks` is false, which is equivalent to
315
- // `x < c`.
324
+ // We will use the format `x <= c - strictnessOffset`. Since `x < c` is
325
+ // the same as `x <= c-1`, we set `strictnessOffset` to 1 in this case.
326
+ // For `x >= c` we will be dealing with the case where the `branch`
327
+ // argument of `checks` is false, which is equivalent to `x < c`.
316
328
if expr instanceof LssExpr or expr instanceof GeqExpr
317
329
then strictnessOffset = 1
318
330
else strictnessOffset = 0
@@ -321,7 +333,10 @@ class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
321
333
then
322
334
any ( MaxIntOrMaxUint m | expr .getAnOperand ( ) = m .getAReference ( ) )
323
335
.isBoundFor ( b , architectureBitSize , strictnessOffset )
324
- else expr .getAnOperand ( ) .getExactValue ( ) .toFloat ( ) - strictnessOffset <= 2 .pow ( b ) - 1
336
+ else
337
+ // We want `x <= c - strictnessOffset` to guarantee that `x <= 2^b - 1`,
338
+ // which is equivalent to saying `c - strictnessOffset <= 2^b - 1`.
339
+ expr .getAnOperand ( ) .getExactValue ( ) .toFloat ( ) - strictnessOffset <= 2 .pow ( b ) - 1
325
340
)
326
341
}
327
342
0 commit comments