Skip to content

Commit dfe00ff

Browse files
committed
C++: Add a flow-after-free library.
1 parent d65bb3b commit dfe00ff

File tree

1 file changed

+113
-0
lines changed

1 file changed

+113
-0
lines changed

cpp/ql/src/Critical/FlowAfterFree.qll

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import cpp
2+
import semmle.code.cpp.dataflow.new.DataFlow
3+
private import semmle.code.cpp.ir.IR
4+
5+
/**
6+
* Signature for a predicate that holds if `n.asExpr() = e` and `n` is a sink in
7+
* the `FlowFromFreeConfig` module.
8+
*/
9+
private signature predicate isSinkSig(DataFlow::Node n, Expr e);
10+
11+
/**
12+
* Holds if `dealloc` is a deallocation expression and `e` is an expression such
13+
* that `isFree(_, e)` holds for some `isFree` predicate satisfying `isSinkSig`,
14+
* and this source-sink pair should be excluded from the analysis.
15+
*/
16+
bindingset[dealloc, e]
17+
private signature predicate isExcludedSig(DeallocationExpr dealloc, Expr e);
18+
19+
/**
20+
* Holds if `(b1, i1)` strictly post-dominates `(b2, i2)`
21+
*/
22+
bindingset[i1, i2]
23+
predicate strictlyPostDominates(IRBlock b1, int i1, IRBlock b2, int i2) {
24+
b1 = b2 and
25+
i1 > i2
26+
or
27+
b1.strictlyPostDominates(b2)
28+
}
29+
30+
/**
31+
* Holds if `(b1, i1)` strictly dominates `(b2, i2)`
32+
*/
33+
bindingset[i1, i2]
34+
predicate strictlyDominates(IRBlock b1, int i1, IRBlock b2, int i2) {
35+
b1 = b2 and
36+
i1 < i2
37+
or
38+
b1.strictlyDominates(b2)
39+
}
40+
41+
/**
42+
* Constructs a `FlowFromFreeConfig` module that can be used to find flow between
43+
* a pointer being freed by some deallocation function, and a user-specified sink.
44+
*
45+
* In order to reduce false positives, the set of sinks is restricted to only those
46+
* that satisfy at least one of the following two criteria:
47+
* 1. The source dominates the sink, or
48+
* 2. The sink post-dominates the source.
49+
*/
50+
module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
51+
module FlowFromFreeConfig implements DataFlow::StateConfigSig {
52+
class FlowState instanceof Expr {
53+
FlowState() { isFree(_, this, _) }
54+
55+
string toString() { result = super.toString() }
56+
}
57+
58+
predicate isSource(DataFlow::Node node, FlowState state) { isFree(node, state, _) }
59+
60+
pragma[inline]
61+
predicate isSink(DataFlow::Node sink, FlowState state) {
62+
exists(
63+
Expr e, DataFlow::Node source, IRBlock b1, int i1, IRBlock b2, int i2,
64+
DeallocationExpr dealloc
65+
|
66+
isASink(sink, e) and
67+
isFree(source, state, dealloc) and
68+
e != state and
69+
source.hasIndexInBlock(b1, i1) and
70+
sink.hasIndexInBlock(b2, i2) and
71+
not isExcluded(dealloc, e)
72+
|
73+
strictlyDominates(b1, i1, b2, i2)
74+
or
75+
strictlyPostDominates(b2, i2, b1, i1)
76+
)
77+
}
78+
79+
predicate isBarrierIn(DataFlow::Node n) {
80+
n.asIndirectExpr() = any(AddressOfExpr aoe)
81+
or
82+
n.asIndirectExpr() = any(Call call).getAnArgument()
83+
or
84+
exists(Expr e |
85+
n.asIndirectExpr() = e.(PointerDereferenceExpr).getOperand() or
86+
n.asIndirectExpr() = e.(ArrayExpr).getArrayBase()
87+
|
88+
e = any(StoreInstruction store).getDestinationAddress().getUnconvertedResultExpression()
89+
)
90+
}
91+
92+
predicate isBarrier(DataFlow::Node n, FlowState state) { none() }
93+
94+
predicate isAdditionalFlowStep(
95+
DataFlow::Node n1, FlowState state1, DataFlow::Node n2, FlowState state2
96+
) {
97+
none()
98+
}
99+
}
100+
101+
import DataFlow::GlobalWithState<FlowFromFreeConfig>
102+
}
103+
104+
/**
105+
* Holds if `n` is a dataflow node such that `n.asExpr() = e` and `e`
106+
* is being freed by a deallocation function `dealloc`.
107+
*/
108+
predicate isFree(DataFlow::Node n, Expr e, DeallocationExpr dealloc) {
109+
e = dealloc.getFreedExpr() and
110+
e = n.asExpr() and
111+
// Ignore realloc functions
112+
not exists(dealloc.(FunctionCall).getTarget().(AllocationFunction).getReallocPtrArg())
113+
}

0 commit comments

Comments
 (0)