Skip to content

Commit d14ad92

Browse files
authored
Merge pull request github#14006 from MathiasVP/promote-invalid-pointer-deref-out-of-experimental
C++: Promote `cpp/invalid-pointer-deref` out of experimental
2 parents f3a77c6 + b948ed9 commit d14ad92

22 files changed

+228
-152
lines changed

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -550,11 +550,14 @@ class SsaPhiNode extends Node, TSsaPhiNode {
550550
* `fromBackEdge` is true if data flows along a back-edge,
551551
* and `false` otherwise.
552552
*/
553+
cached
553554
final Node getAnInput(boolean fromBackEdge) {
554555
localFlowStep(result, this) and
555-
if phi.getBasicBlock().dominates(result.getBasicBlock())
556-
then fromBackEdge = true
557-
else fromBackEdge = false
556+
exists(IRBlock bPhi, IRBlock bResult |
557+
bPhi = phi.getBasicBlock() and bResult = result.getBasicBlock()
558+
|
559+
if bPhi.dominates(bResult) then fromBackEdge = true else fromBackEdge = false
560+
)
558561
}
559562

560563
/** Gets a node that is used as input to this phi node. */

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ProductFlow.qll

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,30 @@ module ProductFlow {
8787
* dataflow graph.
8888
*/
8989
default predicate isBarrierIn2(DataFlow::Node node) { none() }
90+
91+
/**
92+
* Gets the virtual dispatch branching limit when calculating field flow in the first
93+
* projection of the product dataflow graph.
94+
*
95+
* This can be overridden to a smaller value to improve performance (a
96+
* value of 0 disables field flow), or a larger value to get more results.
97+
*/
98+
default int fieldFlowBranchLimit1() {
99+
// NOTE: This should be synchronized with the default value in the shared dataflow library
100+
result = 2
101+
}
102+
103+
/**
104+
* Gets the virtual dispatch branching limit when calculating field flow in the second
105+
* projection of the product dataflow graph.
106+
*
107+
* This can be overridden to a smaller value to improve performance (a
108+
* value of 0 disables field flow), or a larger value to get more results.
109+
*/
110+
default int fieldFlowBranchLimit2() {
111+
// NOTE: This should be synchronized with the default value in the shared dataflow library
112+
result = 2
113+
}
90114
}
91115

92116
/**
@@ -272,6 +296,30 @@ module ProductFlow {
272296
* dataflow graph.
273297
*/
274298
default predicate isBarrierIn2(DataFlow::Node node) { none() }
299+
300+
/**
301+
* Gets the virtual dispatch branching limit when calculating field flow in the first
302+
* projection of the product dataflow graph.
303+
*
304+
* This can be overridden to a smaller value to improve performance (a
305+
* value of 0 disables field flow), or a larger value to get more results.
306+
*/
307+
default int fieldFlowBranchLimit1() {
308+
// NOTE: This should be synchronized with the default value in the shared dataflow library
309+
result = 2
310+
}
311+
312+
/**
313+
* Gets the virtual dispatch branching limit when calculating field flow in the second
314+
* projection of the product dataflow graph.
315+
*
316+
* This can be overridden to a smaller value to improve performance (a
317+
* value of 0 disables field flow), or a larger value to get more results.
318+
*/
319+
default int fieldFlowBranchLimit2() {
320+
// NOTE: This should be synchronized with the default value in the shared dataflow library
321+
result = 2
322+
}
275323
}
276324

277325
/**
@@ -335,6 +383,8 @@ module ProductFlow {
335383
}
336384

337385
predicate isBarrierIn(DataFlow::Node node) { Config::isBarrierIn1(node) }
386+
387+
int fieldFlowBranchLimit() { result = Config::fieldFlowBranchLimit1() }
338388
}
339389

340390
private module Flow1 = DataFlow::GlobalWithState<Config1>;
@@ -367,6 +417,8 @@ module ProductFlow {
367417
}
368418

369419
predicate isBarrierIn(DataFlow::Node node) { Config::isBarrierIn2(node) }
420+
421+
int fieldFlowBranchLimit() { result = Config::fieldFlowBranchLimit2() }
370422
}
371423

372424
private module Flow2 = DataFlow::GlobalWithState<Config2>;
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
2-
* This file contains the range-analysis specific parts of the `cpp/invalid-pointer-deref` query
3-
* that is used by both `AllocationToInvalidPointer.qll` and `InvalidPointerToDereference.qll`.
2+
* This file contains the range-analysis specific parts of the `cpp/invalid-pointer-deref`
3+
* and `cpp/overrun-write` query.
44
*/
55

66
private import cpp

cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticGuard.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ predicate semImplies_v2(SemGuard g1, boolean b1, SemGuard g2, boolean b2) {
3939
* Holds if `guard` directly controls the position `controlled` with the
4040
* value `testIsTrue`.
4141
*/
42+
pragma[nomagic]
4243
predicate semGuardDirectlyControlsSsaRead(
4344
SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue
4445
) {

cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/analysis/ModulusAnalysis.qll

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,25 @@ private import RangeUtils
1717
private import RangeAnalysisStage
1818

1919
module ModulusAnalysis<DeltaSig D, BoundSig<D> Bounds, UtilSig<D> U> {
20+
pragma[nomagic]
21+
private predicate valueFlowStepSsaEqFlowCond(
22+
SemSsaReadPosition pos, SemSsaVariable v, SemExpr e, int delta
23+
) {
24+
exists(SemGuard guard, boolean testIsTrue |
25+
guard = U::semEqFlowCond(v, e, D::fromInt(delta), true, testIsTrue) and
26+
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue)
27+
)
28+
}
29+
2030
/**
2131
* Holds if `e + delta` equals `v` at `pos`.
2232
*/
33+
pragma[nomagic]
2334
private predicate valueFlowStepSsa(SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta) {
2435
U::semSsaUpdateStep(v, e, D::fromInt(delta)) and pos.hasReadOfVar(v)
2536
or
26-
exists(SemGuard guard, boolean testIsTrue |
27-
pos.hasReadOfVar(v) and
28-
guard = U::semEqFlowCond(v, e, D::fromInt(delta), true, testIsTrue) and
29-
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue)
30-
)
37+
pos.hasReadOfVar(v) and
38+
valueFlowStepSsaEqFlowCond(pos, v, e, delta)
3139
}
3240

3341
/**

cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/analysis/RangeAnalysisStage.qll

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ module RangeStage<
660660
* - `upper = false` : `v >= b + delta`
661661
*/
662662
private predicate boundedSsa(
663-
SemSsaVariable v, SemSsaReadPosition pos, SemBound b, D::Delta delta, boolean upper,
663+
SemSsaVariable v, SemBound b, D::Delta delta, SemSsaReadPosition pos, boolean upper,
664664
boolean fromBackEdge, D::Delta origdelta, SemReason reason
665665
) {
666666
exists(SemExpr mid, D::Delta d1, D::Delta d2, SemReason r1, SemReason r2 |
@@ -673,10 +673,13 @@ module RangeStage<
673673
)
674674
or
675675
exists(D::Delta d, SemReason r1, SemReason r2 |
676-
boundedSsa(v, pos, b, d, upper, fromBackEdge, origdelta, r2) or
677-
boundedPhi(v, b, d, upper, fromBackEdge, origdelta, r2)
676+
boundedSsa(pragma[only_bind_into](v), pragma[only_bind_into](b), pragma[only_bind_into](d),
677+
pragma[only_bind_into](pos), upper, fromBackEdge, origdelta, r2)
678+
or
679+
boundedPhi(pragma[only_bind_into](v), pragma[only_bind_into](b), pragma[only_bind_into](d),
680+
upper, fromBackEdge, origdelta, r2)
678681
|
679-
unequalIntegralSsa(v, pos, b, d, r1) and
682+
unequalIntegralSsa(v, b, d, pos, r1) and
680683
(
681684
upper = true and delta = D::fromFloat(D::toFloat(d) - 1)
682685
or
@@ -694,7 +697,7 @@ module RangeStage<
694697
* Holds if `v != b + delta` at `pos` and `v` is of integral type.
695698
*/
696699
private predicate unequalIntegralSsa(
697-
SemSsaVariable v, SemSsaReadPosition pos, SemBound b, D::Delta delta, SemReason reason
700+
SemSsaVariable v, SemBound b, D::Delta delta, SemSsaReadPosition pos, SemReason reason
698701
) {
699702
exists(SemExpr e, D::Delta d1, D::Delta d2 |
700703
unequalFlowStepIntegralSsa(v, pos, e, d1, reason) and
@@ -746,7 +749,7 @@ module RangeStage<
746749
) {
747750
edge.phiInput(phi, inp) and
748751
exists(D::Delta d, boolean fromBackEdge0 |
749-
boundedSsa(inp, edge, b, d, upper, fromBackEdge0, origdelta, reason)
752+
boundedSsa(inp, b, d, edge, upper, fromBackEdge0, origdelta, reason)
750753
or
751754
boundedPhi(inp, b, d, upper, fromBackEdge0, origdelta, reason)
752755
or
@@ -1022,7 +1025,7 @@ module RangeStage<
10221025
reason = TSemNoReason()
10231026
or
10241027
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
1025-
boundedSsa(v, bb, b, delta, upper, fromBackEdge, origdelta, reason) and
1028+
boundedSsa(v, b, delta, bb, upper, fromBackEdge, origdelta, reason) and
10261029
e = v.getAUse() and
10271030
bb.getBlock() = e.getBasicBlock()
10281031
)

cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/analysis/RangeUtils.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ module RangeUtil<Range::DeltaSig D, Range::LangSig<D> Lang> implements Range::Ut
4949
* - `isEq = true` : `v == e + delta`
5050
* - `isEq = false` : `v != e + delta`
5151
*/
52+
pragma[nomagic]
5253
SemGuard semEqFlowCond(
5354
SemSsaVariable v, SemExpr e, D::Delta delta, boolean isEq, boolean testIsTrue
5455
) {

cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/AllocationToInvalidPointer.qll

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ private import semmle.code.cpp.ir.dataflow.internal.ProductFlow
5656
private import semmle.code.cpp.ir.ValueNumbering
5757
private import semmle.code.cpp.controlflow.IRGuards
5858
private import codeql.util.Unit
59-
private import RangeAnalysisUtil
59+
private import semmle.code.cpp.rangeanalysis.new.RangeAnalysisUtil
6060

6161
private VariableAccess getAVariableAccess(Expr e) { e.getAChild*() = result }
6262

@@ -77,6 +77,15 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
7777
)
7878
}
7979

80+
/**
81+
* Gets the virtual dispatch branching limit when calculating field flow while searching
82+
* for flow from an allocation to the construction of an out-of-bounds pointer.
83+
*
84+
* This can be overridden to a smaller value to improve performance (a
85+
* value of 0 disables field flow), or a larger value to get more results.
86+
*/
87+
int allocationToInvalidPointerFieldFlowBranchLimit() { result = 0 }
88+
8089
/**
8190
* A module that encapsulates a barrier guard to remove false positives from flow like:
8291
* ```cpp
@@ -101,9 +110,12 @@ private module SizeBarrier {
101110
predicate isSource(DataFlow::Node source) {
102111
// The sources is the same as in the sources for the second
103112
// projection in the `AllocToInvalidPointerConfig` module.
104-
hasSize(_, source, _)
113+
hasSize(_, source, _) and
114+
InterestingPointerAddInstruction::isInterestingSize(source)
105115
}
106116

117+
int fieldFlowBranchLimit() { result = allocationToInvalidPointerFieldFlowBranchLimit() }
118+
107119
/**
108120
* Holds if `small <= large + k` holds if `g` evaluates to `testIsTrue`.
109121
*/
@@ -201,6 +213,8 @@ private module InterestingPointerAddInstruction {
201213
hasSize(source.asConvertedExpr(), _, _)
202214
}
203215

216+
int fieldFlowBranchLimit() { result = allocationToInvalidPointerFieldFlowBranchLimit() }
217+
204218
predicate isSink(DataFlow::Node sink) {
205219
sink.asInstruction() = any(PointerAddInstruction pai).getLeft()
206220
}
@@ -220,6 +234,19 @@ private module InterestingPointerAddInstruction {
220234
flowTo(n)
221235
)
222236
}
237+
238+
/**
239+
* Holds if `n` is a size of an allocation whose result flows to the left operand
240+
* of a pointer-arithmetic instruction.
241+
*
242+
* This predicate is used to reduce the set of tuples in `SizeBarrierConfig::isSource`.
243+
*/
244+
predicate isInterestingSize(DataFlow::Node n) {
245+
exists(DataFlow::Node alloc |
246+
hasSize(alloc.asConvertedExpr(), n, _) and
247+
flow(alloc, _)
248+
)
249+
}
223250
}
224251

225252
/**
@@ -244,6 +271,10 @@ private module Config implements ProductFlow::StateConfigSig {
244271
hasSize(allocSource.asConvertedExpr(), sizeSource, sizeAddend)
245272
}
246273

274+
int fieldFlowBranchLimit1() { result = allocationToInvalidPointerFieldFlowBranchLimit() }
275+
276+
int fieldFlowBranchLimit2() { result = allocationToInvalidPointerFieldFlowBranchLimit() }
277+
247278
predicate isSinkPair(
248279
DataFlow::Node allocSink, FlowState1 unit, DataFlow::Node sizeSink, FlowState2 sizeAddend
249280
) {

cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/InvalidPointerToDereference.qll

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,17 @@ private import semmle.code.cpp.dataflow.new.DataFlow
8181
private import semmle.code.cpp.ir.ValueNumbering
8282
private import semmle.code.cpp.controlflow.IRGuards
8383
private import AllocationToInvalidPointer as AllocToInvalidPointer
84-
private import RangeAnalysisUtil
84+
private import semmle.code.cpp.rangeanalysis.new.RangeAnalysisUtil
85+
86+
/**
87+
* Gets the virtual dispatch branching limit when calculating field flow while
88+
* searching for flow from an out-of-bounds pointer to a dereference of the
89+
* pointer.
90+
*
91+
* This can be overridden to a smaller value to improve performance (a
92+
* value of 0 disables field flow), or a larger value to get more results.
93+
*/
94+
int invalidPointerToDereferenceFieldFlowBranchLimit() { result = 0 }
8595

8696
private module InvalidPointerToDerefBarrier {
8797
private module BarrierConfig implements DataFlow::ConfigSig {
@@ -101,6 +111,8 @@ private module InvalidPointerToDerefBarrier {
101111
}
102112

103113
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
114+
115+
int fieldFlowBranchLimit() { result = invalidPointerToDereferenceFieldFlowBranchLimit() }
104116
}
105117

106118
private module BarrierFlow = DataFlow::Global<BarrierConfig>;
@@ -178,6 +190,8 @@ private module InvalidPointerToDerefConfig implements DataFlow::StateConfigSig {
178190
// Note that this is the only place where the `FlowState` is used in this configuration.
179191
node = InvalidPointerToDerefBarrier::getABarrierNode(pai)
180192
}
193+
194+
int fieldFlowBranchLimit() { result = invalidPointerToDereferenceFieldFlowBranchLimit() }
181195
}
182196

183197
private import DataFlow::GlobalWithState<InvalidPointerToDerefConfig>

cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,10 @@ import semmle.code.cpp.models.interfaces.Allocation
2020
import semmle.code.cpp.models.interfaces.ArrayFunction
2121
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysis
2222
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific
23+
import semmle.code.cpp.rangeanalysis.new.RangeAnalysisUtil
2324
import StringSizeFlow::PathGraph1
2425
import codeql.util.Unit
2526

26-
pragma[nomagic]
27-
Instruction getABoundIn(SemBound b, IRFunction func) {
28-
getSemanticExpr(result) = b.getExpr(0) and
29-
result.getEnclosingIRFunction() = func
30-
}
31-
32-
/**
33-
* Holds if `i <= b + delta`.
34-
*/
35-
bindingset[i]
36-
pragma[inline_late]
37-
predicate bounded(Instruction i, Instruction b, int delta) {
38-
exists(SemBound bound, IRFunction func |
39-
semBounded(getSemanticExpr(i), bound, delta, true, _) and
40-
b = getABoundIn(bound, func) and
41-
i.getEnclosingIRFunction() = func
42-
)
43-
}
44-
4527
VariableAccess getAVariableAccess(Expr e) { e.getAChild*() = result }
4628

4729
/**

0 commit comments

Comments
 (0)