Skip to content

Commit 013216d

Browse files
authored
C++: exclude widening from VeryLikelyOverrunWrite
This also restrict what we consider "non-trivial" range analysis, as we now require both ends to be non-trivially bounded for signed integers. This avoids false positives stemming from a non trivial upper bound but no meaningful lower bound, for example.
1 parent aac0298 commit 013216d

File tree

1 file changed

+52
-34
lines changed

1 file changed

+52
-34
lines changed

cpp/ql/lib/semmle/code/cpp/commons/Printf.qll

Lines changed: 52 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,17 @@ private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
1212
private newtype TBufferWriteEstimationReason =
1313
TUnspecifiedEstimateReason() or
1414
TTypeBoundsAnalysis() or
15+
TWidenedValueFlowAnalysis() or
1516
TValueFlowAnalysis()
1617

18+
private predicate gradeToReason(int grade, TBufferWriteEstimationReason reason) {
19+
// when combining reasons, lower grade takes precedence
20+
grade = 0 and reason = TUnspecifiedEstimateReason() or
21+
grade = 1 and reason = TTypeBoundsAnalysis() or
22+
grade = 2 and reason = TWidenedValueFlowAnalysis() or
23+
grade = 3 and reason = TValueFlowAnalysis()
24+
}
25+
1726
/**
1827
* A reason for a specific buffer write size estimate.
1928
*/
@@ -32,7 +41,11 @@ abstract class BufferWriteEstimationReason extends TBufferWriteEstimationReason
3241
* Combine estimate reasons. Used to give a reason for the size of a format string
3342
* conversion given reasons coming from its individual specifiers.
3443
*/
35-
abstract BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other);
44+
BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
45+
exists(int grade, int otherGrade | gradeToReason(grade, this) and gradeToReason(otherGrade, other) |
46+
if otherGrade < grade then result = other else result = this
47+
)
48+
}
3649
}
3750

3851
/**
@@ -44,12 +57,6 @@ class UnspecifiedEstimateReason extends BufferWriteEstimationReason, TUnspecifie
4457
override string toString() { result = "UnspecifiedEstimateReason" }
4558

4659
override string getDescription() { result = "no reason specified" }
47-
48-
override BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
49-
// this reason should not be used in format specifiers, so it should not be combined
50-
// with other reasons
51-
none()
52-
}
5360
}
5461

5562
/**
@@ -60,12 +67,26 @@ class TypeBoundsAnalysis extends BufferWriteEstimationReason, TTypeBoundsAnalysi
6067
override string toString() { result = "TypeBoundsAnalysis" }
6168

6269
override string getDescription() { result = "based on type bounds" }
70+
}
6371

64-
override BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
65-
other != TUnspecifiedEstimateReason() and result = TTypeBoundsAnalysis()
66-
}
72+
/**
73+
* The estimation comes from non trivial bounds found via actual flow analysis,
74+
* but a widening aproximation might have been used for variables in loops.
75+
* For example
76+
* ```
77+
* for (int i = 0; i < 10; ++i) {
78+
* int j = i + i;
79+
* //... <- estimation done here based on j
80+
* }
81+
* ```
82+
*/
83+
class WidenedValueFlowAnalysis extends BufferWriteEstimationReason, TWidenedValueFlowAnalysis {
84+
override string toString() { result = "WidenedValueFlowAnalysis" }
85+
86+
override string getDescription() { result = "based on flow analysis of value bounds with a widening approximation" }
6787
}
6888

89+
6990
/**
7091
* The estimation comes from non trivial bounds found via actual flow analysis.
7192
* For example
@@ -80,10 +101,6 @@ class ValueFlowAnalysis extends BufferWriteEstimationReason, TValueFlowAnalysis
80101
override string toString() { result = "ValueFlowAnalysis" }
81102

82103
override string getDescription() { result = "based on flow analysis of value bounds" }
83-
84-
override BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
85-
other != TUnspecifiedEstimateReason() and result = other
86-
}
87104
}
88105

89106
class PrintfFormatAttribute extends FormatAttribute {
@@ -359,6 +376,17 @@ private int lengthInBase10(float f) {
359376
result = f.log10().floor() + 1
360377
}
361378

379+
private BufferWriteEstimationReason getEstimationReasonForIntegralExpression(Expr expr) {
380+
// we consider the range analysis non trivial if it
381+
// * constrained non-trivially both sides of a signed value, or
382+
// * constrained non-trivially the positive side of an unsigned value
383+
// expr should already be given as getFullyConverted
384+
if upperBound(expr) < exprMaxVal(expr) and (exprMinVal(expr) >= 0 or lowerBound(expr) > exprMinVal(expr))
385+
// next we check whether the estimate may have been widened
386+
then if upperBoundMayBeWidened(expr) then result = TWidenedValueFlowAnalysis()
387+
else result = TValueFlowAnalysis()
388+
else result = TTypeBoundsAnalysis()
389+
}
362390
/**
363391
* A class to represent format strings that occur as arguments to invocations of formatting functions.
364392
*/
@@ -1158,11 +1186,9 @@ class FormatLiteral extends Literal {
11581186
// The second case uses range analysis to deduce a length that's shorter than the length
11591187
// of the number -2^31.
11601188
exists(Expr arg, float lower, float upper, float typeLower, float typeUpper |
1161-
arg = this.getUse().getConversionArgument(n) and
1162-
lower = lowerBound(arg.getFullyConverted()) and
1163-
upper = upperBound(arg.getFullyConverted()) and
1164-
typeLower = exprMinVal(arg.getFullyConverted()) and
1165-
typeUpper = exprMaxVal(arg.getFullyConverted())
1189+
arg = this.getUse().getConversionArgument(n).getFullyConverted() and
1190+
lower = lowerBound(arg) and
1191+
upper = upperBound(arg)
11661192
|
11671193
valueBasedBound =
11681194
max(int cand |
@@ -1179,11 +1205,7 @@ class FormatLiteral extends Literal {
11791205
else cand = lengthInBase10(upper)
11801206
)
11811207
) and
1182-
(
1183-
if lower > typeLower or upper < typeUpper
1184-
then reason = TValueFlowAnalysis()
1185-
else reason = TTypeBoundsAnalysis()
1186-
)
1208+
reason = getEstimationReasonForIntegralExpression(arg)
11871209
) and
11881210
len = valueBasedBound.minimum(typeBasedBound)
11891211
)
@@ -1196,11 +1218,11 @@ class FormatLiteral extends Literal {
11961218
// The second case uses range analysis to deduce a length that's shorter than
11971219
// the length of the number 2^31 - 1.
11981220
exists(Expr arg, float lower, float upper, float typeLower, float typeUpper |
1199-
arg = this.getUse().getConversionArgument(n) and
1200-
lower = lowerBound(arg.getFullyConverted()) and
1201-
upper = upperBound(arg.getFullyConverted()) and
1202-
typeLower = exprMinVal(arg.getFullyConverted()) and
1203-
typeUpper = exprMaxVal(arg.getFullyConverted())
1221+
arg = this.getUse().getConversionArgument(n).getFullyConverted() and
1222+
lower = lowerBound(arg) and
1223+
upper = upperBound(arg) and
1224+
typeLower = exprMinVal(arg) and
1225+
typeUpper = exprMaxVal(arg)
12041226
|
12051227
valueBasedBound =
12061228
lengthInBase10(max(float cand |
@@ -1210,11 +1232,7 @@ class FormatLiteral extends Literal {
12101232
or
12111233
cand = upper
12121234
)) and
1213-
(
1214-
if lower > typeLower or upper < typeUpper
1215-
then reason = TValueFlowAnalysis()
1216-
else reason = TTypeBoundsAnalysis()
1217-
)
1235+
reason = getEstimationReasonForIntegralExpression(arg)
12181236
) and
12191237
len = valueBasedBound.minimum(typeBasedBound)
12201238
)

0 commit comments

Comments
 (0)