@@ -22,8 +22,6 @@ import semmle.code.cpp.ir.IR
22
22
import semmle.code.cpp.security.FlowSources
23
23
import semmle.code.cpp.models.implementations.Strcat
24
24
25
- import DataFlow:: PathGraph
26
-
27
25
Expr sinkAsArgumentIndirection ( DataFlow:: Node sink ) {
28
26
result =
29
27
sink .asOperand ( )
@@ -54,7 +52,7 @@ predicate interestingConcatenation(DataFlow::Node fst, DataFlow::Node snd) {
54
52
rse .getArgumentDef ( ) = call .getArgument ( strcatFunc .getParamSrc ( ) ) and
55
53
fst .asOperand ( ) = rse .getSideEffectOperand ( ) and
56
54
snd .asInstruction ( ) .( WriteSideEffectInstruction ) .getDestinationAddress ( ) =
57
- call .getArgument ( strcatFunc .getParamDest ( ) )
55
+ call .getArgument ( strcatFunc .getParamDest ( ) )
58
56
)
59
57
or
60
58
exists ( CallInstruction call , Operator op , ReadSideEffectInstruction rse |
@@ -63,22 +61,17 @@ predicate interestingConcatenation(DataFlow::Node fst, DataFlow::Node snd) {
63
61
op .getType ( ) .( UserType ) .hasQualifiedName ( "std" , "basic_string" ) and
64
62
call .getArgument ( 1 ) = rse .getArgumentOperand ( ) .getAnyDef ( ) and // left operand
65
63
fst .asOperand ( ) = rse .getSideEffectOperand ( ) and
66
- call =
67
- snd .asInstruction ( )
64
+ call = snd .asInstruction ( )
68
65
)
69
66
}
70
67
71
68
// TODO: maybe we can drop this?
72
69
class TaintToConcatenationConfiguration extends TaintTracking:: Configuration {
73
70
TaintToConcatenationConfiguration ( ) { this = "TaintToConcatenationConfiguration" }
74
71
75
- override predicate isSource ( DataFlow:: Node source ) {
76
- source instanceof FlowSource
77
- }
72
+ override predicate isSource ( DataFlow:: Node source ) { source instanceof FlowSource }
78
73
79
- override predicate isSink ( DataFlow:: Node sink ) {
80
- interestingConcatenation ( sink , _)
81
- }
74
+ override predicate isSink ( DataFlow:: Node sink ) { interestingConcatenation ( sink , _) }
82
75
83
76
override predicate isSanitizer ( DataFlow:: Node node ) {
84
77
node .asInstruction ( ) .getResultType ( ) instanceof IntegralType
@@ -90,9 +83,7 @@ class TaintToConcatenationConfiguration extends TaintTracking::Configuration {
90
83
class ExecTaintConfiguration extends TaintTracking2:: Configuration {
91
84
ExecTaintConfiguration ( ) { this = "ExecTaintConfiguration" }
92
85
93
- override predicate isSource ( DataFlow:: Node source ) {
94
- interestingConcatenation ( _, source )
95
- }
86
+ override predicate isSource ( DataFlow:: Node source ) { interestingConcatenation ( _, source ) }
96
87
97
88
override predicate isSink ( DataFlow:: Node sink ) {
98
89
shellCommand ( sinkAsArgumentIndirection ( sink ) , _)
@@ -103,16 +94,105 @@ class ExecTaintConfiguration extends TaintTracking2::Configuration {
103
94
}
104
95
}
105
96
97
+ module StitchedPathGraph {
98
+ // There's a different PathNode class for each DataFlowImplN.qll, so we can't simply combine the
99
+ // PathGraph predicates directly. Instead, we use a newtype so there's a single type that
100
+ // contains both sets of PathNodes.
101
+ newtype TMergedPathNode =
102
+ TPathNode1 ( DataFlow:: PathNode node ) or
103
+ TPathNode2 ( DataFlow2:: PathNode node )
104
+
105
+ // this wraps the toString and location predicates so we can use the merged node type in a
106
+ // selection
107
+ class MergedPathNode extends TMergedPathNode {
108
+ string toString ( ) {
109
+ exists ( DataFlow:: PathNode n |
110
+ this = TPathNode1 ( n ) and
111
+ result = n .toString ( )
112
+ )
113
+ or
114
+ exists ( DataFlow2:: PathNode n |
115
+ this = TPathNode2 ( n ) and
116
+ result = n .toString ( )
117
+ )
118
+ }
119
+
120
+ DataFlow:: Node getNode ( ) {
121
+ exists ( DataFlow:: PathNode n |
122
+ this = TPathNode1 ( n ) and
123
+ result = n .getNode ( )
124
+ )
125
+ or
126
+ exists ( DataFlow2:: PathNode n |
127
+ this = TPathNode2 ( n ) and
128
+ result = n .getNode ( )
129
+ )
130
+ }
131
+
132
+ predicate hasLocationInfo (
133
+ string filepath , int startline , int startcolumn , int endline , int endcolumn
134
+ ) {
135
+ exists ( DataFlow:: PathNode n |
136
+ this = TPathNode1 ( n ) and
137
+ n .hasLocationInfo ( filepath , startline , startcolumn , endline , endcolumn )
138
+ )
139
+ or
140
+ exists ( DataFlow2:: PathNode n |
141
+ this = TPathNode2 ( n ) and
142
+ n .hasLocationInfo ( filepath , startline , startcolumn , endline , endcolumn )
143
+ )
144
+ }
145
+ }
146
+
147
+ query predicate edges ( MergedPathNode a , MergedPathNode b ) {
148
+ exists ( DataFlow:: PathNode an , DataFlow:: PathNode bn |
149
+ a = TPathNode1 ( an ) and
150
+ b = TPathNode1 ( bn ) and
151
+ DataFlow:: PathGraph:: edges ( an , bn )
152
+ )
153
+ or
154
+ exists ( DataFlow2:: PathNode an , DataFlow2:: PathNode bn |
155
+ a = TPathNode2 ( an ) and
156
+ b = TPathNode2 ( bn ) and
157
+ DataFlow2:: PathGraph:: edges ( an , bn )
158
+ )
159
+ or
160
+ // This is where paths from the two configurations are connected. `interestingConcatenation`
161
+ // is the only thing in this module that's actually specific to the query - everything else is
162
+ // just using types and predicates from the DataFlow library.
163
+ interestingConcatenation ( a .getNode ( ) , b .getNode ( ) ) and
164
+ a instanceof TPathNode1 and
165
+ b instanceof TPathNode2
166
+ }
167
+
168
+ query predicate nodes ( MergedPathNode mpn , string key , string val ) {
169
+ // here we just need the union of the underlying `nodes` predicates
170
+ exists ( DataFlow:: PathNode n |
171
+ mpn = TPathNode1 ( n ) and
172
+ DataFlow:: PathGraph:: nodes ( n , key , val )
173
+ )
174
+ or
175
+ exists ( DataFlow2:: PathNode n |
176
+ mpn = TPathNode2 ( n ) and
177
+ DataFlow2:: PathGraph:: nodes ( n , key , val )
178
+ )
179
+ }
180
+ }
181
+
182
+ import StitchedPathGraph
183
+
106
184
from
107
- DataFlow:: PathNode sourceNode , DataFlow:: PathNode concatSink , DataFlow2:: PathNode concatSource , DataFlow2:: PathNode sinkNode , string taintCause , string callChain ,
185
+ DataFlow:: PathNode sourceNode , DataFlow:: PathNode concatSink , DataFlow2:: PathNode concatSource ,
186
+ DataFlow2:: PathNode sinkNode , string taintCause , string callChain ,
108
187
TaintToConcatenationConfiguration conf1 , ExecTaintConfiguration conf2
109
188
where
110
189
taintCause = sourceNode .getNode ( ) .( FlowSource ) .getSourceType ( ) and
111
- conf1 .hasFlowPath ( sourceNode , concatSink ) and // TODO: can we link these better?
112
- interestingConcatenation ( concatSink .getNode ( ) , concatSource .getNode ( ) ) and
190
+ conf1 .hasFlowPath ( sourceNode , concatSink ) and
191
+ interestingConcatenation ( concatSink .getNode ( ) , concatSource .getNode ( ) ) and // this loses call context
113
192
conf2 .hasFlowPath ( concatSource , sinkNode ) and
114
193
shellCommand ( sinkAsArgumentIndirection ( sinkNode .getNode ( ) ) , callChain )
115
- select sinkAsArgumentIndirection ( sinkNode .getNode ( ) ) , sourceNode , sinkNode ,
116
- "This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to " + callChain , sourceNode ,
117
- "user input (" + taintCause + ")" , concatSource , concatSource .toString ( )
118
-
194
+ select sinkAsArgumentIndirection ( sinkNode .getNode ( ) ) , TPathNode1 ( sourceNode ) .( MergedPathNode ) ,
195
+ TPathNode2 ( sinkNode ) .( MergedPathNode ) ,
196
+ "This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to "
197
+ + callChain , sourceNode , "user input (" + taintCause + ")" , concatSource ,
198
+ concatSource .toString ( )
0 commit comments