Skip to content

Commit 0f4157c

Browse files
committed
C++: Add overflow detection to new range analysis
1 parent 834511b commit 0f4157c

File tree

4 files changed

+136
-54
lines changed

4 files changed

+136
-54
lines changed

cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/FloatDelta.qll

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
private import RangeAnalysisStage
2+
private import RangeAnalysisImpl
23

34
module FloatDelta implements DeltaSig {
45
class Delta = float;
@@ -27,3 +28,44 @@ module FloatDelta implements DeltaSig {
2728
)
2829
}
2930
}
31+
32+
module FloatOverflow implements OverflowSig<FloatDelta> {
33+
predicate semExprDoesntOverflow(boolean positively, SemExpr expr) {
34+
exists(float lb, float ub, float delta |
35+
typeBounds(expr.getSemType(), lb, ub) and
36+
ConstantStage::initialBounded(expr, any(ConstantBounds::SemZeroBound b), delta, positively.booleanNot(), _, _, _)
37+
|
38+
positively = true and delta < ub
39+
or
40+
positively = false and delta > lb
41+
)
42+
}
43+
/*
44+
predicate semExprOverflow(float delta, boolean upper, SemExpr expr) {
45+
exists(float lb, float ub | typeBounds(expr.getSemType(), lb, ub) |
46+
upper = false and delta < lb
47+
or
48+
upper = true and delta > ub
49+
)
50+
}
51+
*/
52+
53+
predicate typeBounds(SemType t, float lb, float ub) {
54+
exists(SemIntegerType integralType, float limit |
55+
integralType = t and limit = 2.pow(8 * integralType.getByteSize())
56+
|
57+
if integralType instanceof SemBooleanType
58+
then lb = 0 and ub = 1
59+
else
60+
if integralType.isSigned()
61+
then (
62+
lb = -(limit / 2) and ub = (limit / 2) - 1
63+
) else (
64+
lb = 0 and ub = limit - 1
65+
)
66+
)
67+
or
68+
// This covers all floating point types. The range is (-Inf, +Inf).
69+
t instanceof SemFloatingPointType and lb = -(1.0 / 0.0) and ub = 1.0 / 0.0
70+
}
71+
}

cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysisImpl.qll

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
private import RangeAnalysisStage
1+
private import RangeAnalysisStage
22
private import RangeAnalysisSpecific
33
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
44
private import RangeUtils
@@ -46,11 +46,11 @@ private module RelativeBounds implements BoundSig<FloatDelta> {
4646
}
4747
}
4848

49-
private module ConstantStage =
50-
RangeStage<FloatDelta, ConstantBounds, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
49+
module ConstantStage =
50+
RangeStage<FloatDelta, ConstantBounds, FloatOverflow, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
5151

52-
private module RelativeStage =
53-
RangeStage<FloatDelta, RelativeBounds, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
52+
module RelativeStage =
53+
RangeStage<FloatDelta, RelativeBounds, FloatOverflow, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
5454

5555
private newtype TSemReason =
5656
TSemNoReason() or

cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/RangeAnalysisStage.qll

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,18 @@ signature module BoundSig<DeltaSig D> {
243243
}
244244
}
245245

246-
module RangeStage<DeltaSig D, BoundSig<D> Bounds, LangSig<D> LangParam, UtilSig<D> UtilParam> {
246+
signature module OverflowSig<DeltaSig D> {
247+
predicate semExprDoesntOverflow(boolean positively, SemExpr expr);
248+
}
249+
250+
module RangeStage<
251+
DeltaSig D, BoundSig<D> Bounds, OverflowSig<D> OverflowParam, LangSig<D> LangParam,
252+
UtilSig<D> UtilParam> {
247253
private import Bounds
248254
private import LangParam
249255
private import UtilParam
250256
private import D
257+
private import OverflowParam
251258

252259
/**
253260
* An expression that does conversion, boxing, or unboxing
@@ -941,6 +948,41 @@ module RangeStage<DeltaSig D, BoundSig<D> Bounds, LangSig<D> LangParam, UtilSig<
941948
bounded(cast.getOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
942949
}
943950

951+
predicate bounded(
952+
SemExpr e, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge, D::Delta origdelta,
953+
SemReason reason) {
954+
initialBounded(e, b, delta, upper, fromBackEdge, origdelta, reason) and
955+
(
956+
semExprDoesntOverflow(upper.booleanNot(), e)
957+
or
958+
not potentiallyOverflowingExpr(upper.booleanNot(), e)
959+
)
960+
}
961+
962+
predicate potentiallyOverflowingExpr(boolean positively, SemExpr expr) {
963+
positively in [true, false] and
964+
(
965+
expr.getOpcode() instanceof Opcode::Add or
966+
expr.getOpcode() instanceof Opcode::PointerAdd or
967+
expr.getOpcode() instanceof Opcode::Sub or
968+
expr.getOpcode() instanceof Opcode::PointerSub or
969+
expr.getOpcode() instanceof Opcode::Mul or
970+
expr.getOpcode() instanceof Opcode::ShiftLeft
971+
)
972+
or
973+
positively = false and
974+
(
975+
expr.getOpcode() instanceof Opcode::Negate or
976+
expr.getOpcode() instanceof Opcode::SubOne
977+
)
978+
or
979+
positively = false and
980+
expr.(SemDivExpr).getSemType() instanceof SemFloatingPointType
981+
or
982+
positively = true and
983+
expr.getOpcode() instanceof Opcode::AddOne
984+
}
985+
944986
/**
945987
* Computes a normal form of `x` where -0.0 has changed to +0.0. This can be
946988
* needed on the lesser side of a floating-point comparison or on both sides of
@@ -955,7 +997,7 @@ module RangeStage<DeltaSig D, BoundSig<D> Bounds, LangSig<D> LangParam, UtilSig<
955997
* - `upper = true` : `e <= b + delta`
956998
* - `upper = false` : `e >= b + delta`
957999
*/
958-
private predicate bounded(
1000+
predicate initialBounded(
9591001
SemExpr e, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge, D::Delta origdelta,
9601002
SemReason reason
9611003
) {

0 commit comments

Comments
 (0)