Skip to content

Commit 7828452

Browse files
committed
dbg
1 parent bf42971 commit 7828452

File tree

2 files changed

+115
-215
lines changed

2 files changed

+115
-215
lines changed

rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql

Lines changed: 109 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -18,90 +18,130 @@ import codeql.rust.dataflow.TaintTracking
1818
import codeql.rust.security.AccessAfterLifetimeExtensions::AccessAfterLifetime
1919
import AccessAfterLifetimeFlow::PathGraph
2020

21-
/**
22-
* A data flow configuration for detecting accesses to a pointer after its
23-
* lifetime has ended.
24-
*/
25-
module AccessAfterLifetimeConfig implements DataFlow::ConfigSig {
26-
predicate isSource(DataFlow::Node node) {
27-
node instanceof Source and
28-
// exclude cases with sources in macros, since these results are difficult to interpret
29-
not node.asExpr().isFromMacroExpansion() and
30-
sourceValueScope(node, _, _)
31-
}
21+
signature predicate sourceSinkPairSig(DataFlow::Node source, DataFlow::Node sink);
3222

33-
predicate isSink(DataFlow::Node node) {
34-
node instanceof Sink and
35-
// Exclude cases with sinks in macros, since these results are difficult to interpret
36-
not node.asExpr().isFromMacroExpansion() and
37-
// TODO: Remove this condition if it can be done without negatively
38-
// impacting performance. This condition only include nodes with
39-
// corresponding to an expression. This excludes sinks from models-as-data.
40-
exists(node.asExpr())
41-
}
23+
module Config<sourceSinkPairSig/2 sourceSinkPair> {
24+
/**
25+
* A data flow configuration for detecting accesses to a pointer after its
26+
* lifetime has ended.
27+
*/
28+
module AccessAfterLifetimeConfig implements DataFlow::ConfigSig {
29+
predicate isSource(DataFlow::Node node) {
30+
node instanceof Source and
31+
sourceSinkPair(node, _) and
32+
// exclude cases with sources in macros, since these results are difficult to interpret
33+
not node.asExpr().isFromMacroExpansion() and
34+
sourceValueScope(node, _, _)
35+
}
4236

43-
predicate isBarrier(DataFlow::Node barrier) { barrier instanceof Barrier }
37+
predicate isSink(DataFlow::Node node) {
38+
node instanceof Sink and
39+
sourceSinkPair(_, node) and
40+
// Exclude cases with sinks in macros, since these results are difficult to interpret
41+
not node.asExpr().isFromMacroExpansion() and
42+
// TODO: Remove this condition if it can be done without negatively
43+
// impacting performance. This condition only include nodes with
44+
// corresponding to an expression. This excludes sinks from models-as-data.
45+
exists(node.asExpr())
46+
}
4447

45-
predicate observeDiffInformedIncrementalMode() { any() }
48+
predicate isBarrier(DataFlow::Node barrier) { barrier instanceof Barrier }
4649

47-
Location getASelectedSourceLocation(DataFlow::Node source) {
48-
exists(Variable target |
49-
sourceValueScope(source, target, _) and
50-
result = [target.getLocation(), source.getLocation()]
51-
)
50+
predicate observeDiffInformedIncrementalMode() { any() }
51+
52+
Location getASelectedSourceLocation(DataFlow::Node source) {
53+
exists(Variable target |
54+
sourceValueScope(source, target, _) and
55+
result = [target.getLocation(), source.getLocation()]
56+
)
57+
}
5258
}
5359
}
5460

55-
module AccessAfterLifetimeFlow = TaintTracking::Global<AccessAfterLifetimeConfig>;
61+
pragma[inline]
62+
predicate anySourceSinkPair(DataFlow::Node source, DataFlow::Node sink) { any() }
63+
64+
module AccessAfterLifetimeConfig1 = Config<anySourceSinkPair/2>::AccessAfterLifetimeConfig;
65+
66+
module AccessAfterLifetimeFlow1 = TaintTracking::Global<AccessAfterLifetimeConfig1>;
5667

57-
predicate sourceBlock(Source s, Variable target, BlockExpr be) {
58-
AccessAfterLifetimeFlow::flow(s, _) and
59-
sourceValueScope(s, target, be.getEnclosingBlock*())
68+
predicate relevantSourceSinkPairStage3(DataFlow::Node source, DataFlow::Node sink) {
69+
exists(
70+
AccessAfterLifetimeFlow1::Stages::Stage3::Graph::Public::PathNode sourceNode,
71+
AccessAfterLifetimeFlow1::Stages::Stage3::Graph::Public::PathNode sinkNode
72+
|
73+
AccessAfterLifetimeFlow1::Stages::Stage3::Graph::Public::flowPath(sourceNode, sinkNode) and
74+
sourceNode.getNode() = source and
75+
sinkNode.getNode() = sink
76+
)
6077
}
6178

62-
predicate sinkBlock(Sink s, BlockExpr be) {
63-
AccessAfterLifetimeFlow::flow(_, s) and
64-
be = s.asExpr().getEnclosingBlock()
79+
predicate relevantSourceSinkPair(DataFlow::Node source, DataFlow::Node sink) {
80+
Restrict<relevantSourceSinkPairStage3/2>::dereferenceAfterLifetime(source, sink, _)
6581
}
6682

67-
private predicate tcStep(BlockExpr a, BlockExpr b) {
68-
// propagate through function calls
69-
exists(Call call |
70-
a = call.getEnclosingBlock() and
71-
call.getARuntimeTarget() = b.getEnclosingCallable()
72-
)
83+
module AccessAfterLifetimeConfig2 = Config<relevantSourceSinkPair/2>::AccessAfterLifetimeConfig;
84+
85+
module AccessAfterLifetimeFlow2 = TaintTracking::Global<AccessAfterLifetimeConfig2>;
86+
87+
predicate relevantSourceSinkPair2(DataFlow::Node source, DataFlow::Node sink) {
88+
Restrict<AccessAfterLifetimeFlow2::flow/2>::dereferenceAfterLifetime(source, sink, _)
7389
}
7490

75-
private predicate isTcSource(BlockExpr be) { sourceBlock(_, _, be) }
91+
module AccessAfterLifetimeConfig3 = Config<relevantSourceSinkPair2/2>::AccessAfterLifetimeConfig;
7692

77-
private predicate isTcSink(BlockExpr be) { sinkBlock(_, be) }
93+
module AccessAfterLifetimeFlow = TaintTracking::Global<AccessAfterLifetimeConfig3>;
7894

79-
/**
80-
* Holds if block `a` contains block `b`, in the sense that a stack allocated variable in
81-
* `a` may still be on the stack during execution of `b`. This is interprocedural,
82-
* but is an overapproximation that doesn't accurately track call contexts
83-
* (for example if `f` and `g` both call `b`, then depending on the
84-
* caller a variable in `f` or `g` may or may-not be on the stack during `b`).
85-
*/
86-
private predicate mayEncloseOnStack(BlockExpr a, BlockExpr b) =
87-
doublyBoundedFastTC(tcStep/2, isTcSource/1, isTcSink/1)(a, b)
95+
module Restrict<sourceSinkPairSig/2 sourceSinkPair> {
96+
predicate sourceBlock(Source s, Variable target, BlockExpr be) {
97+
sourceSinkPair(s, _) and
98+
sourceValueScope(s, target, be.getEnclosingBlock*())
99+
}
88100

89-
/**
90-
* Holds if the pair `(source, sink)`, that represents a flow from a
91-
* pointer or reference to a dereference, has its dereference outside the
92-
* lifetime of the target variable `target`.
93-
*/
94-
predicate dereferenceAfterLifetime(Source source, Sink sink, Variable target) {
95-
AccessAfterLifetimeFlow::flow(source, sink) and
96-
sourceValueScope(source, target, _) and
97-
not exists(BlockExpr beSource, BlockExpr beSink |
98-
sourceBlock(source, target, beSource) and
99-
sinkBlock(sink, beSink)
100-
|
101-
beSource = beSink
102-
or
103-
mayEncloseOnStack(beSource, beSink)
104-
)
101+
predicate sinkBlock(Sink s, BlockExpr be) {
102+
sourceSinkPair(_, s) and
103+
be = s.asExpr().getEnclosingBlock()
104+
}
105+
106+
private predicate tcStep(BlockExpr a, BlockExpr b) {
107+
// propagate through function calls
108+
exists(Call call |
109+
a = call.getEnclosingBlock() and
110+
call.getARuntimeTarget() = b.getEnclosingCallable()
111+
)
112+
}
113+
114+
private predicate isTcSource(BlockExpr be) { sourceBlock(_, _, be) }
115+
116+
private predicate isTcSink(BlockExpr be) { sinkBlock(_, be) }
117+
118+
/**
119+
* Holds if block `a` contains block `b`, in the sense that a stack allocated variable in
120+
* `a` may still be on the stack during execution of `b`. This is interprocedural,
121+
* but is an overapproximation that doesn't accurately track call contexts
122+
* (for example if `f` and `g` both call `b`, then depending on the
123+
* caller a variable in `f` or `g` may or may-not be on the stack during `b`).
124+
*/
125+
private predicate mayEncloseOnStack(BlockExpr a, BlockExpr b) =
126+
doublyBoundedFastTC(tcStep/2, isTcSource/1, isTcSink/1)(a, b)
127+
128+
/**
129+
* Holds if the pair `(source, sink)`, that represents a flow from a
130+
* pointer or reference to a dereference, has its dereference outside the
131+
* lifetime of the target variable `target`.
132+
*/
133+
predicate dereferenceAfterLifetime(Source source, Sink sink, Variable target) {
134+
sourceSinkPair(source, sink) and
135+
sourceValueScope(source, target, _) and
136+
not exists(BlockExpr beSource, BlockExpr beSink |
137+
sourceBlock(source, target, beSource) and
138+
sinkBlock(sink, beSink)
139+
|
140+
beSource = beSink
141+
or
142+
mayEncloseOnStack(beSource, beSink)
143+
)
144+
}
105145
}
106146

107147
from
@@ -111,6 +151,7 @@ where
111151
// flow from a pointer or reference to the dereference
112152
AccessAfterLifetimeFlow::flowPath(sourceNode, sinkNode) and
113153
// check that the dereference is outside the lifetime of the target
114-
dereferenceAfterLifetime(sourceNode.getNode(), sinkNode.getNode(), target)
154+
Restrict<AccessAfterLifetimeFlow::flow/2>::dereferenceAfterLifetime(sourceNode.getNode(),
155+
sinkNode.getNode(), target)
115156
select sinkNode.getNode(), sourceNode, sinkNode,
116157
"Access of a pointer to $@ after its lifetime has ended.", target, target.toString()

0 commit comments

Comments
 (0)