Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9ed1510

Browse files
committed
C++: Improved query precision using SimpleRangeAnalysis
1 parent e926966 commit 9ed1510

2 files changed

Lines changed: 104 additions & 35 deletions

File tree

cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql

Lines changed: 101 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import cpp
2020
import semmle.code.cpp.controlflow.SSA
21+
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
22+
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
2123

2224
/**
2325
* Holds if `e` is either:
@@ -64,29 +66,105 @@ int getEffectiveMulOperands(MulExpr me) {
6466
)
6567
}
6668

67-
class LeafExpr extends Expr {
68-
LeafExpr() { not this instanceof BinaryOperation }
69+
/**
70+
* As SimpleRangeAnalysis does not support reasoning about multiplication
71+
* we create a tiny abstract interpreter for handling multiplication, which
72+
* we invoke only after weeding out of all of trivial cases that we do
73+
* not care about. By default, the maximum and minimum values are computed
74+
* using SimpleRangeAnalysis.
75+
*/
76+
class AnalyzableExpr extends Expr {
77+
float maxValue() { result = upperBound(this.getFullyConverted()) }
78+
79+
float minValue() { result = lowerBound(this.getFullyConverted()) }
80+
}
81+
82+
class ParenAnalyzableExpr extends AnalyzableExpr, ParenthesisExpr {
83+
override float maxValue() { result = this.getExpr().(AnalyzableExpr).maxValue() }
84+
85+
override float minValue() { result = this.getExpr().(AnalyzableExpr).minValue() }
86+
}
87+
88+
class MulAnalyzableExpr extends AnalyzableExpr, MulExpr {
89+
override float maxValue() {
90+
exists(float x1, float y1, float x2, float y2 |
91+
x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and
92+
x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
93+
y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and
94+
y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
95+
result = (x1 * y1).maximum(x1 * y2).maximum(x2 * y1).maximum(x2 * y2)
96+
)
97+
}
98+
99+
override float minValue() {
100+
exists(float x1, float x2, float y1, float y2 |
101+
x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and
102+
x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
103+
y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and
104+
y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
105+
result = (x1 * y1).minimum(x1 * y2).minimum(x2 * y1).minimum(x2 * y2)
106+
)
107+
}
108+
}
109+
110+
class AddAnalyzableExpr extends AnalyzableExpr, AddExpr {
111+
override float maxValue() {
112+
result = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() +
113+
this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue()
114+
}
115+
116+
override float minValue() {
117+
result = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() +
118+
this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue()
119+
}
120+
}
121+
122+
class SubAnalyzableExpr extends AnalyzableExpr, SubExpr {
123+
override float maxValue() {
124+
result = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() -
125+
this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue()
126+
}
127+
128+
override float minValue() {
129+
result = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() -
130+
this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue()
131+
}
132+
}
133+
134+
class VarAnalyzableExpr extends AnalyzableExpr, VariableAccess {
135+
override float maxValue() {
136+
exists(SsaDefinition def, Variable v |
137+
def.getAUse(v) = this and
138+
// if there is a defining expression, use that for
139+
// computing the maximum value. Otherwise, assign the
140+
// variable the largest possible value it can hold
141+
if exists(def.getDefiningValue(v))
142+
then result = def.getDefiningValue(v).(AnalyzableExpr).maxValue()
143+
else result = exprMaxVal(this)
144+
)
145+
}
146+
147+
override float minValue() {
148+
exists(SsaDefinition def, Variable v |
149+
def.getAUse(v) = this and
150+
if exists(def.getDefiningValue(v))
151+
then result = def.getDefiningValue(v).(AnalyzableExpr).minValue()
152+
else result = exprMinVal(this)
153+
)
154+
}
69155
}
70156

71157
/**
72-
* Gets the size of the largest type (in bytes) of an non-literal
73-
* operand to a binary operation. For example, for
74-
* ```
75-
* unsigned char a; short b;
76-
* (a + 1) * b;
77-
* ```
78-
* the result is 2 (i.e., the size of a `short`)
158+
* Holds if `t` is not an instance of `IntegralType`,
159+
* or if `me` cannot be proven to not overflow
79160
*/
80-
int widenedFromLiteralFromSize(Expr e) {
81-
exists(Literal lit |
82-
lit = e.(BinaryOperation).getAnOperand+() and
83-
result = max(LeafExpr other |
84-
other = e.(BinaryOperation).getAnOperand+() and
85-
not other instanceof Literal
86-
|
87-
other.getType().getSize()
88-
) and
89-
result < lit.getType().getSize()
161+
predicate overflows(MulExpr me, Type t) {
162+
t instanceof IntegralType
163+
implies
164+
(
165+
me.(MulAnalyzableExpr).maxValue() > exprMaxVal(me)
166+
or
167+
me.(MulAnalyzableExpr).minValue() < exprMinVal(me)
90168
)
91169
}
92170

@@ -128,20 +206,10 @@ where
128206
) and
129207
e.(Literal).getType().getSize() = t2.getSize()
130208
) and
131-
// exclude cases where the operands to the multiplication were
132-
// small, but widened due to a literal in a subexpression.
133-
// For instance, if the multiplication is
134-
// ```
135-
// int c = (a + 1) * (b + 1)
136-
// ```
137-
// where a and b are both unsigned chars. In this case the maximum
138-
// value of c will be 256 * 256, even though the arguments to the
139-
// multiplication are both typed as int.
140-
not exists(Expr e1, Expr e2 |
141-
e1 = me.getLeftOperand() and
142-
e2 = me.getRightOperand() and
143-
widenedFromLiteralFromSize(e1) + widenedFromLiteralFromSize(e2) < t1.getSize()
144-
)
209+
// only report if cannot prove that the result of the
210+
// multiplication will be less (resp. greater) than the
211+
// maximum (resp. minimum) number we can store.
212+
overflows(me, t1)
145213
select me,
146214
"Multiplication result may overflow '" + me.getType().toString() + "' before it is converted to '"
147215
+ me.getFullyConverted().getType().toString() + "'."

cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.expected

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
| IntMultToLong.c:61:23:61:33 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. |
88
| IntMultToLong.c:63:23:63:40 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. |
99
| IntMultToLong.c:75:9:75:13 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'size_t'. |
10-
| IntMultToLong.c:99:9:99:25 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'unsigned long'. |
11-
| IntMultToLong.c:103:9:103:31 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'unsigned long'. |
10+
| IntMultToLong.c:99:14:99:35 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'unsigned long'. |
11+
| IntMultToLong.c:103:14:103:46 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'unsigned long'. |
12+
| IntMultToLong.c:108:14:108:78 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'unsigned long'. |

0 commit comments

Comments
 (0)