@@ -18,90 +18,130 @@ import codeql.rust.dataflow.TaintTracking
1818import codeql.rust.security.AccessAfterLifetimeExtensions:: AccessAfterLifetime
1919import 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
107147from
@@ -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 )
115156select sinkNode .getNode ( ) , sourceNode , sinkNode ,
116157 "Access of a pointer to $@ after its lifetime has ended." , target , target .toString ( )
0 commit comments