Skip to content

Commit 896a3c6

Browse files
committed
Avoid doing float arithmetic with large integers
There is the possibility of overflow.
1 parent 570ca3b commit 896a3c6

File tree

1 file changed

+48
-23
lines changed

1 file changed

+48
-23
lines changed

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

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,47 @@
11
import go
22

3-
/**
4-
* Gets the maximum value of an integer (signed if `isSigned`
5-
* is true, unsigned otherwise) with `bitSize` bits.
6-
*/
7-
float getMaxIntValue(int bitSize, boolean isSigned) {
8-
bitSize in [8, 16, 32, 64] and
9-
(
10-
isSigned = true and result = 2.pow(bitSize - 1) - 1
11-
or
12-
isSigned = false and result = 2.pow(bitSize) - 1
13-
)
3+
/** The constant `math.MaxInt` or the constant `math.MaxUint`. */
4+
abstract private class MaxIntOrMaxUint extends DeclaredConstant {
5+
/**
6+
* Gets the integer `x` such that `2.pow(x) - 1` is the value of this
7+
* constant when the architecture has bit size `architectureBitSize`.
8+
*/
9+
abstract int getX(int architectureBitSize);
10+
11+
/**
12+
* Holds if the value of this constant given `architectureBitSize` minus
13+
* `strictnessOffset` is less than or equal to `2.pow(b) - 1`.
14+
*/
15+
predicate isBoundFor(int b, int architectureBitSize, float strictnessOffset) {
16+
// 2.pow(x) - 1 - strictnessOffset <= 2.pow(b) - 1
17+
exists(int x |
18+
x = this.getX(architectureBitSize) and
19+
b = validBitSize() and
20+
(
21+
strictnessOffset = 0 and x <= b
22+
or
23+
strictnessOffset = 1 and x <= b - 1
24+
)
25+
)
26+
}
27+
}
28+
29+
/** The constant `math.MaxInt`. */
30+
private class MaxInt extends MaxIntOrMaxUint {
31+
MaxInt() { this.hasQualifiedName("math", "MaxInt") }
32+
33+
override int getX(int architectureBitSize) {
34+
architectureBitSize = [32, 64] and result = architectureBitSize - 1
35+
}
36+
}
37+
38+
/** The constant `math.MaxUint`. */
39+
private class MaxUint extends MaxIntOrMaxUint {
40+
MaxUint() { this.hasQualifiedName("math", "MaxUint") }
41+
42+
override int getX(int architectureBitSize) {
43+
architectureBitSize = [32, 64] and result = architectureBitSize
44+
}
1445
}
1546

1647
/**
@@ -276,7 +307,7 @@ class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
276307
predicate isBoundFor(int bitSize, int architectureBitSize) {
277308
bitSize = validBitSize() and
278309
architectureBitSize = [32, 64] and
279-
exists(float bound, int b, float strictnessOffset |
310+
exists(int b, float strictnessOffset |
280311
b = max(int a | a = validBitSize() and a < bitSize) and
281312
// For `x < c` the bound is `c-1`. For `x >= c` we will be an upper bound
282313
// on the `branch` argument of `checks` is false, which is equivalent to
@@ -285,17 +316,11 @@ class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
285316
then strictnessOffset = 1
286317
else strictnessOffset = 0
287318
|
288-
exists(DeclaredConstant maxint, DeclaredConstant maxuint |
289-
maxint.hasQualifiedName("math", "MaxInt") and maxuint.hasQualifiedName("math", "MaxUint")
290-
|
291-
if expr.getAnOperand() = maxint.getAReference()
292-
then bound = getMaxIntValue(architectureBitSize, true)
293-
else
294-
if expr.getAnOperand() = maxuint.getAReference()
295-
then bound = getMaxIntValue(architectureBitSize, false)
296-
else bound = expr.getAnOperand().getExactValue().toFloat()
297-
) and
298-
bound - strictnessOffset <= 2.pow(b) - 1
319+
if expr.getAnOperand() = any(MaxIntOrMaxUint m).getAReference()
320+
then
321+
any(MaxIntOrMaxUint m | expr.getAnOperand() = m.getAReference())
322+
.isBoundFor(b, architectureBitSize, strictnessOffset)
323+
else expr.getAnOperand().getExactValue().toFloat() - strictnessOffset <= 2.pow(b) - 1
299324
)
300325
}
301326

0 commit comments

Comments
 (0)