@@ -19,9 +19,9 @@ import semmle.code.cpp.security.Security
19
19
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
20
20
import semmle.code.cpp.ir.IR
21
21
import semmle.code.cpp.ir.dataflow.TaintTracking
22
- import semmle.code.cpp.ir.dataflow.TaintTracking2
23
22
import semmle.code.cpp.security.FlowSources
24
23
import semmle.code.cpp.models.implementations.Strcat
24
+ import DataFlow:: PathGraph
25
25
26
26
Expr sinkAsArgumentIndirection ( DataFlow:: Node sink ) {
27
27
result =
@@ -66,154 +66,70 @@ predicate interestingConcatenation(DataFlow::Node fst, DataFlow::Node snd) {
66
66
)
67
67
}
68
68
69
- class TaintToConcatenationConfiguration extends TaintTracking:: Configuration {
70
- TaintToConcatenationConfiguration ( ) { this = "TaintToConcatenationConfiguration" }
71
-
72
- override predicate isSource ( DataFlow:: Node source ) { source instanceof FlowSource }
73
-
74
- override predicate isSink ( DataFlow:: Node sink ) { interestingConcatenation ( sink , _) }
75
-
76
- override predicate isSanitizer ( DataFlow:: Node node ) {
77
- node .asInstruction ( ) .getResultType ( ) instanceof IntegralType
78
- or
79
- node .asInstruction ( ) .getResultType ( ) instanceof FloatingPointType
80
- }
69
+ class ConcatState extends DataFlow:: FlowState {
70
+ ConcatState ( ) { this = "ConcatState" }
81
71
}
82
72
83
- class ExecTaintConfiguration extends TaintTracking2:: Configuration {
84
- ExecTaintConfiguration ( ) { this = "ExecTaintConfiguration" }
73
+ class ExecState extends DataFlow:: FlowState {
74
+ DataFlow:: Node fst ;
75
+ DataFlow:: Node snd ;
85
76
86
- override predicate isSource ( DataFlow:: Node source ) {
87
- exists ( DataFlow:: Node prevSink , TaintToConcatenationConfiguration conf |
88
- conf .hasFlow ( _, prevSink ) and
89
- interestingConcatenation ( prevSink , source )
90
- )
77
+ ExecState ( ) {
78
+ this =
79
+ "ExecState (" + fst .getLocation ( ) + " | " + fst + ", " + snd .getLocation ( ) + " | " + snd + ")" and
80
+ interestingConcatenation ( fst , snd )
91
81
}
92
82
93
- override predicate isSink ( DataFlow:: Node sink ) {
94
- shellCommand ( sinkAsArgumentIndirection ( sink ) , _)
95
- }
83
+ DataFlow:: Node getFstNode ( ) { result = fst }
96
84
97
- override predicate isSanitizerOut ( DataFlow:: Node node ) {
98
- isSink ( node ) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
99
- }
85
+ DataFlow:: Node getSndNode ( ) { result = snd }
100
86
}
101
87
102
- module StitchedPathGraph {
103
- // There's a different PathNode class for each DataFlowImplN.qll, so we can't simply combine the
104
- // PathGraph predicates directly. Instead, we use a newtype so there's a single type that
105
- // contains both sets of PathNodes.
106
- newtype TMergedPathNode =
107
- TPathNode1 ( DataFlow:: PathNode node ) or
108
- TPathNode2 ( DataFlow2:: PathNode node )
109
-
110
- // this wraps the toString and location predicates so we can use the merged node type in a
111
- // selection
112
- class MergedPathNode extends TMergedPathNode {
113
- string toString ( ) {
114
- exists ( DataFlow:: PathNode n |
115
- this = TPathNode1 ( n ) and
116
- result = n .toString ( )
117
- )
118
- or
119
- exists ( DataFlow2:: PathNode n |
120
- this = TPathNode2 ( n ) and
121
- result = n .toString ( )
122
- )
123
- }
124
-
125
- DataFlow:: Node getNode ( ) {
126
- exists ( DataFlow:: PathNode n |
127
- this = TPathNode1 ( n ) and
128
- result = n .getNode ( )
129
- )
130
- or
131
- exists ( DataFlow2:: PathNode n |
132
- this = TPathNode2 ( n ) and
133
- result = n .getNode ( )
134
- )
135
- }
136
-
137
- DataFlow:: PathNode getPathNode1 ( ) { this = TPathNode1 ( result ) }
88
+ class ExecTaintConfiguration extends TaintTracking:: Configuration {
89
+ ExecTaintConfiguration ( ) { this = "ExecTaintConfiguration" }
138
90
139
- DataFlow2:: PathNode getPathNode2 ( ) { this = TPathNode2 ( result ) }
91
+ override predicate isSource ( DataFlow:: Node source , DataFlow:: FlowState state ) {
92
+ source instanceof FlowSource and
93
+ state instanceof ConcatState
94
+ }
140
95
141
- predicate hasLocationInfo (
142
- string filepath , int startline , int startcolumn , int endline , int endcolumn
143
- ) {
144
- exists ( DataFlow:: PathNode n |
145
- this = TPathNode1 ( n ) and
146
- n .hasLocationInfo ( filepath , startline , startcolumn , endline , endcolumn )
147
- )
148
- or
149
- exists ( DataFlow2:: PathNode n |
150
- this = TPathNode2 ( n ) and
151
- n .hasLocationInfo ( filepath , startline , startcolumn , endline , endcolumn )
152
- )
153
- }
96
+ override predicate isSink ( DataFlow:: Node sink , DataFlow:: FlowState state ) {
97
+ shellCommand ( sinkAsArgumentIndirection ( sink ) , _) and
98
+ state instanceof ExecState
154
99
}
155
100
156
- query predicate edges ( MergedPathNode a , MergedPathNode b ) {
157
- exists ( DataFlow:: PathNode an , DataFlow:: PathNode bn |
158
- a = TPathNode1 ( an ) and
159
- b = TPathNode1 ( bn ) and
160
- DataFlow:: PathGraph:: edges ( an , bn )
161
- )
162
- or
163
- exists ( DataFlow2:: PathNode an , DataFlow2:: PathNode bn |
164
- a = TPathNode2 ( an ) and
165
- b = TPathNode2 ( bn ) and
166
- DataFlow2:: PathGraph:: edges ( an , bn )
167
- )
168
- or
169
- // This is where paths from the two configurations are connected. `interestingConcatenation`
170
- // is the only thing in this module that's actually specific to the query - everything else is
171
- // just using types and predicates from the DataFlow library.
172
- interestingConcatenation ( a .getNode ( ) , b .getNode ( ) ) and
173
- a instanceof TPathNode1 and
174
- b instanceof TPathNode2
101
+ override predicate isAdditionalTaintStep (
102
+ DataFlow:: Node node1 , DataFlow:: FlowState state1 , DataFlow:: Node node2 ,
103
+ DataFlow:: FlowState state2
104
+ ) {
105
+ state1 instanceof ConcatState and
106
+ state2 .( ExecState ) .getFstNode ( ) = node1 and
107
+ state2 .( ExecState ) .getSndNode ( ) = node2
175
108
}
176
109
177
- query predicate nodes ( MergedPathNode mpn , string key , string val ) {
178
- // here we just need the union of the underlying `nodes` predicates
179
- exists ( DataFlow:: PathNode n |
180
- mpn = TPathNode1 ( n ) and
181
- DataFlow:: PathGraph:: nodes ( n , key , val )
182
- )
183
- or
184
- exists ( DataFlow2:: PathNode n |
185
- mpn = TPathNode2 ( n ) and
186
- DataFlow2:: PathGraph:: nodes ( n , key , val )
187
- )
110
+ override predicate isSanitizer ( DataFlow:: Node node , DataFlow:: FlowState state ) {
111
+ (
112
+ node .asInstruction ( ) .getResultType ( ) instanceof IntegralType
113
+ or
114
+ node .asInstruction ( ) .getResultType ( ) instanceof FloatingPointType
115
+ ) and
116
+ state instanceof ConcatState
188
117
}
189
118
190
- query predicate subpaths (
191
- MergedPathNode arg , MergedPathNode par , MergedPathNode ret , MergedPathNode out
192
- ) {
193
- // just forward subpaths from the underlying libraries. This might be slightly awkward when
194
- // the concatenation is deep in a call chain.
195
- DataFlow:: PathGraph:: subpaths ( arg .getPathNode1 ( ) , par .getPathNode1 ( ) , ret .getPathNode1 ( ) ,
196
- out .getPathNode1 ( ) )
197
- or
198
- DataFlow2:: PathGraph:: subpaths ( arg .getPathNode2 ( ) , par .getPathNode2 ( ) , ret .getPathNode2 ( ) ,
199
- out .getPathNode2 ( ) )
119
+ override predicate isSanitizerOut ( DataFlow:: Node node , DataFlow:: FlowState state ) {
120
+ isSink ( node , state ) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
200
121
}
201
122
}
202
123
203
- import StitchedPathGraph
204
-
205
124
from
206
- DataFlow:: PathNode sourceNode , DataFlow:: PathNode concatSink , DataFlow2:: PathNode concatSource ,
207
- DataFlow2:: PathNode sinkNode , string taintCause , string callChain ,
208
- TaintToConcatenationConfiguration conf1 , ExecTaintConfiguration conf2
125
+ ExecTaintConfiguration conf , DataFlow:: PathNode sourceNode , DataFlow:: PathNode sinkNode ,
126
+ string taintCause , string callChain , DataFlow:: Node concatResult
209
127
where
128
+ conf .hasFlowPath ( sourceNode , sinkNode ) and
210
129
taintCause = sourceNode .getNode ( ) .( FlowSource ) .getSourceType ( ) and
211
- conf1 .hasFlowPath ( sourceNode , concatSink ) and
212
- interestingConcatenation ( concatSink .getNode ( ) , concatSource .getNode ( ) ) and // this loses call context
213
- conf2 .hasFlowPath ( concatSource , sinkNode ) and
214
- shellCommand ( sinkAsArgumentIndirection ( sinkNode .getNode ( ) ) , callChain )
215
- select sinkAsArgumentIndirection ( sinkNode .getNode ( ) ) , TPathNode1 ( sourceNode ) .( MergedPathNode ) ,
216
- TPathNode2 ( sinkNode ) .( MergedPathNode ) ,
130
+ shellCommand ( sinkAsArgumentIndirection ( sinkNode .getNode ( ) ) , callChain ) and
131
+ concatResult = sinkNode .getState ( ) .( ExecState ) .getSndNode ( )
132
+ select sinkAsArgumentIndirection ( sinkNode .getNode ( ) ) , sourceNode , sinkNode ,
217
133
"This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to "
218
- + callChain , sourceNode , "user input (" + taintCause + ")" , concatSource ,
219
- concatSource .toString ( )
134
+ + callChain , sourceNode , "user input (" + taintCause + ")" , concatResult ,
135
+ concatResult .toString ( )
0 commit comments