1515import rust
1616import codeql.rust.dataflow.DataFlow
1717import codeql.rust.dataflow.TaintTracking
18- import codeql.rust.security.AccessAfterLifetimeExtensions
18+ import codeql.rust.security.AccessAfterLifetimeExtensions:: AccessAfterLifetime
1919import AccessAfterLifetimeFlow:: PathGraph
2020
2121/**
@@ -24,14 +24,14 @@ import AccessAfterLifetimeFlow::PathGraph
2424 */
2525module 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
5555module 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+
57132from
58133 AccessAfterLifetimeFlow:: PathNode sourceNode , AccessAfterLifetimeFlow:: PathNode sinkNode ,
59134 Variable target
60135where
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 )
66140select sinkNode .getNode ( ) , sourceNode , sinkNode ,
67141 "Access of a pointer to $@ after its lifetime has ended." , target , target .toString ( )
0 commit comments