Skip to content

Commit 19179d5

Browse files
authored
Merge pull request #21071 from hvitved/rust/access-after-lifetime-perf
Rust: Speedup `AccessAfterLifetime.ql`
2 parents 16b2e71 + 836b667 commit 19179d5

File tree

2 files changed

+57
-39
lines changed

2 files changed

+57
-39
lines changed

rust/ql/lib/codeql/rust/security/AccessAfterLifetimeExtensions.qll

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,6 @@ module AccessAfterLifetime {
4747
valueScope(source.getTarget(), target, scope)
4848
}
4949

50-
/**
51-
* Holds if the pair `(source, sink)`, that represents a flow from a
52-
* pointer or reference to a dereference, has its dereference outside the
53-
* lifetime of the target variable `target`.
54-
*/
55-
bindingset[source, sink]
56-
predicate dereferenceAfterLifetime(Source source, Sink sink, Variable target) {
57-
exists(BlockExpr valueScope, BlockExpr accessScope |
58-
sourceValueScope(source, target, valueScope) and
59-
accessScope = sink.asExpr().getEnclosingBlock() and
60-
not mayEncloseOnStack(valueScope, accessScope)
61-
)
62-
}
63-
6450
/**
6551
* Holds if `var` has scope `scope`.
6652
*/
@@ -88,24 +74,6 @@ module AccessAfterLifetime {
8874
valueScope(value.(FieldExpr).getContainer(), target, scope)
8975
}
9076

91-
/**
92-
* Holds if block `a` contains block `b`, in the sense that a stack allocated variable in
93-
* `a` may still be on the stack during execution of `b`. This is interprocedural,
94-
* but is an overapproximation that doesn't accurately track call contexts
95-
* (for example if `f` and `g` both call `b`, then then depending on the
96-
* caller a variable in `f` or `g` may or may-not be on the stack during `b`).
97-
*/
98-
private predicate mayEncloseOnStack(BlockExpr a, BlockExpr b) {
99-
// `b` is a child of `a`
100-
a = b.getEnclosingBlock*()
101-
or
102-
// propagate through function calls
103-
exists(Call call |
104-
mayEncloseOnStack(a, call.getEnclosingBlock()) and
105-
call.getARuntimeTarget() = b.getEnclosingCallable()
106-
)
107-
}
108-
10977
/**
11078
* A source that is a `RefExpr`.
11179
*/

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

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import rust
1616
import codeql.rust.dataflow.DataFlow
1717
import codeql.rust.dataflow.TaintTracking
18-
import codeql.rust.security.AccessAfterLifetimeExtensions
18+
import codeql.rust.security.AccessAfterLifetimeExtensions::AccessAfterLifetime
1919
import AccessAfterLifetimeFlow::PathGraph
2020

2121
/**
@@ -24,14 +24,14 @@ import AccessAfterLifetimeFlow::PathGraph
2424
*/
2525
module AccessAfterLifetimeConfig implements DataFlow::ConfigSig {
2626
predicate isSource(DataFlow::Node node) {
27-
node instanceof AccessAfterLifetime::Source and
27+
node instanceof Source and
2828
// exclude cases with sources in macros, since these results are difficult to interpret
2929
not node.asExpr().isFromMacroExpansion() and
30-
AccessAfterLifetime::sourceValueScope(node, _, _)
30+
sourceValueScope(node, _, _)
3131
}
3232

3333
predicate isSink(DataFlow::Node node) {
34-
node instanceof AccessAfterLifetime::Sink and
34+
node instanceof Sink and
3535
// Exclude cases with sinks in macros, since these results are difficult to interpret
3636
not node.asExpr().isFromMacroExpansion() and
3737
// TODO: Remove this condition if it can be done without negatively
@@ -40,27 +40,77 @@ module AccessAfterLifetimeConfig implements DataFlow::ConfigSig {
4040
exists(node.asExpr())
4141
}
4242

43-
predicate isBarrier(DataFlow::Node barrier) { barrier instanceof AccessAfterLifetime::Barrier }
43+
predicate isBarrier(DataFlow::Node barrier) { barrier instanceof Barrier }
4444

4545
predicate observeDiffInformedIncrementalMode() { any() }
4646

4747
Location getASelectedSourceLocation(DataFlow::Node source) {
4848
exists(Variable target |
49-
AccessAfterLifetime::sourceValueScope(source, target, _) and
49+
sourceValueScope(source, target, _) and
5050
result = [target.getLocation(), source.getLocation()]
5151
)
5252
}
5353
}
5454

5555
module AccessAfterLifetimeFlow = TaintTracking::Global<AccessAfterLifetimeConfig>;
5656

57+
predicate sourceBlock(Source s, Variable target, BlockExpr be) {
58+
AccessAfterLifetimeFlow::flow(s, _) and
59+
sourceValueScope(s, target, be.getEnclosingBlock*())
60+
}
61+
62+
predicate sinkBlock(Sink s, BlockExpr be) {
63+
AccessAfterLifetimeFlow::flow(_, s) and
64+
be = s.asExpr().getEnclosingBlock()
65+
}
66+
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+
)
73+
}
74+
75+
private predicate isTcSource(BlockExpr be) { sourceBlock(_, _, be) }
76+
77+
private predicate isTcSink(BlockExpr be) { sinkBlock(_, be) }
78+
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)
88+
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+
)
105+
}
106+
57107
from
58108
AccessAfterLifetimeFlow::PathNode sourceNode, AccessAfterLifetimeFlow::PathNode sinkNode,
59109
Variable target
60110
where
61111
// flow from a pointer or reference to the dereference
62112
AccessAfterLifetimeFlow::flowPath(sourceNode, sinkNode) and
63113
// check that the dereference is outside the lifetime of the target
64-
AccessAfterLifetime::dereferenceAfterLifetime(sourceNode.getNode(), sinkNode.getNode(), target)
114+
dereferenceAfterLifetime(sourceNode.getNode(), sinkNode.getNode(), target)
65115
select sinkNode.getNode(), sourceNode, sinkNode,
66116
"Access of a pointer to $@ after its lifetime has ended.", target, target.toString()

0 commit comments

Comments
 (0)