@@ -48,17 +48,87 @@ module AccessAfterLifetime {
4848 }
4949
5050 /**
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`.
51+ * Holds if the pair `(source, sink)` represents a flow from a pointer or reference
52+ * to a dereference.
5453 */
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- )
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+ }
62132 }
63133
64134 /**
@@ -88,24 +158,6 @@ module AccessAfterLifetime {
88158 valueScope ( value .( FieldExpr ) .getContainer ( ) , target , scope )
89159 }
90160
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-
109161 /**
110162 * A source that is a `RefExpr`.
111163 */
0 commit comments