Skip to content

Commit 5bc457f

Browse files
committed
Rust: Move logic from AccessAfterLifetimeExtensions.qll to AccessAfterLifetime.ql
1 parent 06a5648 commit 5bc457f

File tree

2 files changed

+82
-92
lines changed

2 files changed

+82
-92
lines changed

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

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

50-
/**
51-
* Holds if the pair `(source, sink)` represents a flow from a pointer or reference
52-
* to a dereference.
53-
*/
54-
signature predicate dereferenceAfterLifetimeCandSig(DataFlow::Node source, DataFlow::Node sink);
55-
56-
/** Provides logic for identifying dereferences after lifetime. */
57-
module DereferenceAfterLifetime<dereferenceAfterLifetimeCandSig/2 dereferenceAfterLifetimeCand> {
58-
private newtype TTcNode =
59-
TSource(Source s, Variable target) {
60-
dereferenceAfterLifetimeCand(s, _) and sourceValueScope(s, target, _)
61-
} or
62-
TBlockExpr(BlockExpr be) or
63-
TSink(Sink s) { dereferenceAfterLifetimeCand(_, s) }
64-
65-
private class TcNode extends TTcNode {
66-
Source asSource(Variable target) { this = TSource(result, target) }
67-
68-
BlockExpr asBlockExpr() { this = TBlockExpr(result) }
69-
70-
Sink asSink() { this = TSink(result) }
71-
72-
string toString() {
73-
result = this.asSource(_).toString()
74-
or
75-
result = this.asBlockExpr().toString()
76-
or
77-
result = this.asSink().toString()
78-
}
79-
80-
Location getLocation() {
81-
result = this.asSource(_).getLocation()
82-
or
83-
result = this.asBlockExpr().getLocation()
84-
or
85-
result = this.asSink().getLocation()
86-
}
87-
}
88-
89-
pragma[nomagic]
90-
private predicate tcStep(TcNode a, TcNode b) {
91-
// `b` is a child of `a`
92-
exists(Source source, Variable target, BlockExpr be |
93-
source = a.asSource(target) and
94-
be = b.asBlockExpr().getEnclosingBlock*() and
95-
sourceValueScope(source, target, be) and
96-
dereferenceAfterLifetimeCand(source, _)
97-
)
98-
or
99-
// propagate through function calls
100-
exists(Call call |
101-
a.asBlockExpr() = call.getEnclosingBlock() and
102-
call.getARuntimeTarget() = b.asBlockExpr().getEnclosingCallable()
103-
)
104-
or
105-
a.asBlockExpr() = b.asSink().asExpr().getEnclosingBlock()
106-
}
107-
108-
private predicate isTcSource(TcNode n) { n instanceof TSource }
109-
110-
private predicate isTcSink(TcNode n) { n instanceof TSink }
111-
112-
/**
113-
* Holds if block `a` contains block `b`, in the sense that a stack allocated variable in
114-
* `a` may still be on the stack during execution of `b`. This is interprocedural,
115-
* but is an overapproximation that doesn't accurately track call contexts
116-
* (for example if `f` and `g` both call `b`, then then depending on the
117-
* caller a variable in `f` or `g` may or may-not be on the stack during `b`).
118-
*/
119-
private predicate mayEncloseOnStack(TcNode a, TcNode b) =
120-
doublyBoundedFastTC(tcStep/2, isTcSource/1, isTcSink/1)(a, b)
121-
122-
/**
123-
* Holds if the pair `(source, sink)`, that represents a flow from a
124-
* pointer or reference to a dereference, has its dereference outside the
125-
* lifetime of the target variable `target`.
126-
*/
127-
predicate dereferenceAfterLifetime(Source source, Sink sink, Variable target) {
128-
dereferenceAfterLifetimeCand(source, sink) and
129-
sourceValueScope(source, target, _) and
130-
not mayEncloseOnStack(TSource(source, target), TSink(sink))
131-
}
132-
}
133-
13450
/**
13551
* Holds if `var` has scope `scope`.
13652
*/

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

Lines changed: 82 additions & 8 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,28 +40,102 @@ 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+
private newtype TTcNode =
58+
TSource(Source s, Variable target) {
59+
AccessAfterLifetimeFlow::flow(s, _) and sourceValueScope(s, target, _)
60+
} or
61+
TBlockExpr(BlockExpr be) or
62+
TSink(Sink s) { AccessAfterLifetimeFlow::flow(_, s) }
63+
64+
private class TcNode extends TTcNode {
65+
Source asSource(Variable target) { this = TSource(result, target) }
66+
67+
BlockExpr asBlockExpr() { this = TBlockExpr(result) }
68+
69+
Sink asSink() { this = TSink(result) }
70+
71+
string toString() {
72+
result = this.asSource(_).toString()
73+
or
74+
result = this.asBlockExpr().toString()
75+
or
76+
result = this.asSink().toString()
77+
}
78+
79+
Location getLocation() {
80+
result = this.asSource(_).getLocation()
81+
or
82+
result = this.asBlockExpr().getLocation()
83+
or
84+
result = this.asSink().getLocation()
85+
}
86+
}
87+
88+
pragma[nomagic]
89+
private predicate tcStep(TcNode a, TcNode b) {
90+
// `b` is a child of `a`
91+
exists(Source source, Variable target, BlockExpr be |
92+
source = a.asSource(target) and
93+
be = b.asBlockExpr().getEnclosingBlock*() and
94+
sourceValueScope(source, target, be) and
95+
AccessAfterLifetimeFlow::flow(source, _)
96+
)
97+
or
98+
// propagate through function calls
99+
exists(Call call |
100+
a.asBlockExpr() = call.getEnclosingBlock() and
101+
call.getARuntimeTarget() = b.asBlockExpr().getEnclosingCallable()
102+
)
103+
or
104+
a.asBlockExpr() = b.asSink().asExpr().getEnclosingBlock()
105+
}
106+
107+
private predicate isTcSource(TcNode n) { n instanceof TSource }
108+
109+
private predicate isTcSink(TcNode n) { n instanceof TSink }
110+
111+
/**
112+
* Holds if block `a` contains block `b`, in the sense that a stack allocated variable in
113+
* `a` may still be on the stack during execution of `b`. This is interprocedural,
114+
* but is an overapproximation that doesn't accurately track call contexts
115+
* (for example if `f` and `g` both call `b`, then then depending on the
116+
* caller a variable in `f` or `g` may or may-not be on the stack during `b`).
117+
*/
118+
private predicate mayEncloseOnStack(TcNode a, TcNode b) =
119+
doublyBoundedFastTC(tcStep/2, isTcSource/1, isTcSink/1)(a, b)
120+
121+
/**
122+
* Holds if the pair `(source, sink)`, that represents a flow from a
123+
* pointer or reference to a dereference, has its dereference outside the
124+
* lifetime of the target variable `target`.
125+
*/
126+
predicate dereferenceAfterLifetime(Source source, Sink sink, Variable target) {
127+
AccessAfterLifetimeFlow::flow(source, sink) and
128+
sourceValueScope(source, target, _) and
129+
not mayEncloseOnStack(TSource(source, target), TSink(sink))
130+
}
131+
57132
from
58133
AccessAfterLifetimeFlow::PathNode sourceNode, AccessAfterLifetimeFlow::PathNode sinkNode,
59134
Variable target
60135
where
61136
// flow from a pointer or reference to the dereference
62137
AccessAfterLifetimeFlow::flowPath(sourceNode, sinkNode) and
63138
// check that the dereference is outside the lifetime of the target
64-
AccessAfterLifetime::DereferenceAfterLifetime<AccessAfterLifetimeFlow::flow/2>::dereferenceAfterLifetime(sourceNode
65-
.getNode(), sinkNode.getNode(), target)
139+
dereferenceAfterLifetime(sourceNode.getNode(), sinkNode.getNode(), target)
66140
select sinkNode.getNode(), sourceNode, sinkNode,
67141
"Access of a pointer to $@ after its lifetime has ended.", target, target.toString()

0 commit comments

Comments
 (0)