Skip to content

Commit 581305b

Browse files
committed
Improve QLDoc for UpperBoundCheckGuard
1 parent 8beacb8 commit 581305b

File tree

1 file changed

+25
-10
lines changed

1 file changed

+25
-10
lines changed

go/ql/lib/semmle/go/security/IncorrectIntegerConversionLib.qll

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -289,17 +289,27 @@ private predicate upperBoundCheckGuard(DataFlow::Node g, Expr e, boolean branch)
289289
/** An upper bound check that compares a variable to a constant value. */
290290
class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
291291
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.
292295
count(expr.getAnOperand().getExactValue()) = 1 and
293296
expr.getAnOperand().getType().getUnderlyingType() instanceof IntegerType
294297
}
295298

296299
/**
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`.
303313
*
304314
* Note that we have to use floats here because integers in CodeQL are
305315
* represented by 32-bit signed integers, which cannot represent some of the
@@ -309,10 +319,12 @@ class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
309319
bitSize = validBitSize() and
310320
architectureBitSize = [32, 64] and
311321
exists(int b, float strictnessOffset |
322+
// It is sufficient to check for the next valid bit size below `bitSize`.
312323
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`.
316328
if expr instanceof LssExpr or expr instanceof GeqExpr
317329
then strictnessOffset = 1
318330
else strictnessOffset = 0
@@ -321,7 +333,10 @@ class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
321333
then
322334
any(MaxIntOrMaxUint m | expr.getAnOperand() = m.getAReference())
323335
.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
325340
)
326341
}
327342

0 commit comments

Comments
 (0)