11import cpp
22import semmle.code.cpp.security.Security
33private import semmle.code.cpp.ir.dataflow.DataFlow
4+ private import semmle.code.cpp.ir.dataflow.DataFlow2
45private import semmle.code.cpp.ir.IR
56private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
67
@@ -29,20 +30,60 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
2930 instructionTaintStep ( n1 .asInstruction ( ) , n2 .asInstruction ( ) )
3031 }
3132
32- override predicate isBarrier ( DataFlow:: Node node ) {
33- exists ( Variable checkedVar |
34- accessesVariable ( node .asInstruction ( ) , checkedVar ) and
35- hasUpperBoundsCheck ( checkedVar )
33+ override predicate isBarrier ( DataFlow:: Node node ) { nodeIsBarrier ( node ) }
34+ }
35+
36+ private class ToGlobalVarTaintTrackingCfg extends DataFlow:: Configuration {
37+ ToGlobalVarTaintTrackingCfg ( ) { this = "GlobalVarTaintTrackingCfg" }
38+
39+ override predicate isSource ( DataFlow:: Node source ) { isUserInput ( source .asExpr ( ) , _) }
40+
41+ override predicate isSink ( DataFlow:: Node sink ) {
42+ exists ( GlobalOrNamespaceVariable gv | writesVariable ( sink .asInstruction ( ) , gv ) )
43+ }
44+
45+ override predicate isAdditionalFlowStep ( DataFlow:: Node n1 , DataFlow:: Node n2 ) {
46+ instructionTaintStep ( n1 .asInstruction ( ) , n2 .asInstruction ( ) )
47+ or
48+ exists ( StoreInstruction i1 , LoadInstruction i2 , GlobalOrNamespaceVariable gv |
49+ writesVariable ( i1 , gv ) and
50+ readsVariable ( i2 , gv ) and
51+ i1 = n1 .asInstruction ( ) and
52+ i2 = n2 .asInstruction ( )
3653 )
3754 }
55+
56+ override predicate isBarrier ( DataFlow:: Node node ) { nodeIsBarrier ( node ) }
3857}
3958
40- private predicate accessesVariable ( CopyInstruction copy , Variable var ) {
41- exists ( VariableAddressInstruction va | va .getASTVariable ( ) = var |
42- copy .( StoreInstruction ) .getDestinationAddress ( ) = va
43- or
44- copy .( LoadInstruction ) .getSourceAddress ( ) = va
45- )
59+ private class FromGlobalVarTaintTrackingCfg extends DataFlow2:: Configuration {
60+ FromGlobalVarTaintTrackingCfg ( ) { this = "FromGlobalVarTaintTrackingCfg" }
61+
62+ override predicate isSource ( DataFlow:: Node source ) {
63+ exists (
64+ ToGlobalVarTaintTrackingCfg other , DataFlow:: Node prevSink , GlobalOrNamespaceVariable gv
65+ |
66+ other .hasFlowTo ( prevSink ) and
67+ writesVariable ( prevSink .asInstruction ( ) , gv ) and
68+ readsVariable ( source .asInstruction ( ) , gv )
69+ )
70+ }
71+
72+ override predicate isSink ( DataFlow:: Node sink ) { any ( ) }
73+
74+ override predicate isAdditionalFlowStep ( DataFlow:: Node n1 , DataFlow:: Node n2 ) {
75+ instructionTaintStep ( n1 .asInstruction ( ) , n2 .asInstruction ( ) )
76+ }
77+
78+ override predicate isBarrier ( DataFlow:: Node node ) { nodeIsBarrier ( node ) }
79+ }
80+
81+ private predicate readsVariable ( LoadInstruction load , Variable var ) {
82+ load .getSourceAddress ( ) .( VariableAddressInstruction ) .getASTVariable ( ) = var
83+ }
84+
85+ private predicate writesVariable ( StoreInstruction store , Variable var ) {
86+ store .getDestinationAddress ( ) .( VariableAddressInstruction ) .getASTVariable ( ) = var
4687}
4788
4889/**
@@ -62,6 +103,13 @@ private predicate hasUpperBoundsCheck(Variable var) {
62103 )
63104}
64105
106+ private predicate nodeIsBarrier ( DataFlow:: Node node ) {
107+ exists ( Variable checkedVar |
108+ readsVariable ( node .asInstruction ( ) , checkedVar ) and
109+ hasUpperBoundsCheck ( checkedVar )
110+ )
111+ }
112+
65113private predicate instructionTaintStep ( Instruction i1 , Instruction i2 ) {
66114 // Expressions computed from tainted data are also tainted
67115 i2 = any ( CallInstruction call |
@@ -99,51 +147,73 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
99147 // addition expression.
100148}
101149
150+ private Element adjustedSink ( DataFlow:: Node sink ) {
151+ // TODO: is it more appropriate to use asConvertedExpr here and avoid
152+ // `getConversion*`? Or will that cause us to miss some cases where there's
153+ // flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
154+ // pretend there was flow to the converted `Expr` for the sake of
155+ // compatibility.
156+ sink .asExpr ( ) .getConversion * ( ) = result
157+ or
158+ // For compatibility, send flow from arguments to parameters, even for
159+ // functions with no body.
160+ exists ( FunctionCall call , int i |
161+ sink .asExpr ( ) = call .getArgument ( i ) and
162+ result = resolveCall ( call ) .getParameter ( i )
163+ )
164+ or
165+ // For compatibility, send flow into a `Variable` if there is flow to any
166+ // Load or Store of that variable.
167+ exists ( CopyInstruction copy |
168+ copy .getSourceValue ( ) = sink .asInstruction ( ) and
169+ (
170+ readsVariable ( copy , result ) or
171+ writesVariable ( copy , result )
172+ ) and
173+ not hasUpperBoundsCheck ( result )
174+ )
175+ or
176+ // For compatibility, send flow into a `NotExpr` even if it's part of a
177+ // short-circuiting condition and thus might get skipped.
178+ result .( NotExpr ) .getOperand ( ) = sink .asExpr ( )
179+ }
180+
102181predicate tainted ( Expr source , Element tainted ) {
103182 exists ( DefaultTaintTrackingCfg cfg , DataFlow:: Node sink |
104183 cfg .hasFlow ( DataFlow:: exprNode ( source ) , sink )
105184 |
106- // TODO: is it more appropriate to use asConvertedExpr here and avoid
107- // `getConversion*`? Or will that cause us to miss some cases where there's
108- // flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
109- // pretend there was flow to the converted `Expr` for the sake of
110- // compatibility.
111- sink .asExpr ( ) .getConversion * ( ) = tainted
112- or
113- // For compatibility, send flow from arguments to parameters, even for
114- // functions with no body.
115- exists ( FunctionCall call , int i |
116- sink .asExpr ( ) = call .getArgument ( i ) and
117- tainted = resolveCall ( call ) .getParameter ( i )
118- )
119- or
120- // For compatibility, send flow into a `Variable` if there is flow to any
121- // Load or Store of that variable.
122- exists ( CopyInstruction copy |
123- copy .getSourceValue ( ) = sink .asInstruction ( ) and
124- accessesVariable ( copy , tainted ) and
125- not hasUpperBoundsCheck ( tainted )
126- )
127- or
128- // For compatibility, send flow into a `NotExpr` even if it's part of a
129- // short-circuiting condition and thus might get skipped.
130- tainted .( NotExpr ) .getOperand ( ) = sink .asExpr ( )
185+ tainted = adjustedSink ( sink )
131186 )
132187}
133188
134189predicate taintedIncludingGlobalVars ( Expr source , Element tainted , string globalVar ) {
135190 tainted ( source , tainted ) and
136- // TODO: Find a way to emulate how `security.TaintTracking` reports the last
137- // global variable that taint has passed through. Also make sure we emulate
138- // its behavior for interprocedural flow through globals.
139191 globalVar = ""
192+ or
193+ exists (
194+ ToGlobalVarTaintTrackingCfg toCfg , FromGlobalVarTaintTrackingCfg fromCfg , DataFlow:: Node store ,
195+ GlobalOrNamespaceVariable global , DataFlow:: Node load , DataFlow:: Node sink
196+ |
197+ toCfg .hasFlow ( DataFlow:: exprNode ( source ) , store ) and
198+ store
199+ .asInstruction ( )
200+ .( StoreInstruction )
201+ .getDestinationAddress ( )
202+ .( VariableAddressInstruction )
203+ .getASTVariable ( ) = global and
204+ load
205+ .asInstruction ( )
206+ .( LoadInstruction )
207+ .getSourceAddress ( )
208+ .( VariableAddressInstruction )
209+ .getASTVariable ( ) = global and
210+ fromCfg .hasFlow ( load , sink ) and
211+ tainted = adjustedSink ( sink ) and
212+ global = globalVarFromId ( globalVar )
213+ )
140214}
141215
142- GlobalOrNamespaceVariable globalVarFromId ( string id ) {
143- // TODO: Implement this when `taintedIncludingGlobalVars` has support for
144- // global variables.
145- none ( )
146- }
216+ GlobalOrNamespaceVariable globalVarFromId ( string id ) { id = result .getQualifiedName ( ) }
147217
148218Function resolveCall ( Call call ) {
149219 exists ( CallInstruction callInstruction |
0 commit comments