@@ -21,6 +21,40 @@ private predicate predictableInstruction(Instruction instr) {
2121 predictableInstruction ( instr .( UnaryInstruction ) .getUnary ( ) )
2222}
2323
24+ /**
25+ * Functions that we should only allow taint to flow through (to the return
26+ * value) if all but the source argument are 'predictable'. This is done to
27+ * emulate the old security library's implementation rather than due to any
28+ * strong belief that this is the right approach.
29+ *
30+ * Note that the list itself is not very principled; it consists of all the
31+ * functions listed in the old security library's [default] `isPureFunction`
32+ * that have more than one argument, but are not in the old taint tracking
33+ * library's `returnArgument` predicate. In addition, `strlen` is included
34+ * because it's also a special case in flow to return values.
35+ */
36+ predicate predictableOnlyFlow ( string name ) {
37+ name = "strcasestr" or
38+ name = "strchnul" or
39+ name = "strchr" or
40+ name = "strchrnul" or
41+ name = "strcmp" or
42+ name = "strcspn" or
43+ name = "strlen" or // special case
44+ name = "strncmp" or
45+ name = "strndup" or
46+ name = "strnlen" or
47+ name = "strrchr" or
48+ name = "strspn" or
49+ name = "strstr" or
50+ name = "strtod" or
51+ name = "strtof" or
52+ name = "strtol" or
53+ name = "strtoll" or
54+ name = "strtoq" or
55+ name = "strtoul"
56+ }
57+
2458private DataFlow:: Node getNodeForSource ( Expr source ) {
2559 isUserInput ( source , _) and
2660 (
@@ -35,7 +69,7 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
3569
3670 override predicate isSource ( DataFlow:: Node source ) { source = getNodeForSource ( _) }
3771
38- override predicate isSink ( DataFlow:: Node sink ) { any ( ) }
72+ override predicate isSink ( DataFlow:: Node sink ) { exists ( adjustedSink ( sink ) ) }
3973
4074 override predicate isAdditionalFlowStep ( DataFlow:: Node n1 , DataFlow:: Node n2 ) {
4175 instructionTaintStep ( n1 .asInstruction ( ) , n2 .asInstruction ( ) )
@@ -50,18 +84,15 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
5084 override predicate isSource ( DataFlow:: Node source ) { source = getNodeForSource ( _) }
5185
5286 override predicate isSink ( DataFlow:: Node sink ) {
53- exists ( GlobalOrNamespaceVariable gv | writesVariable ( sink .asInstruction ( ) , gv ) )
87+ sink .asVariable ( ) instanceof GlobalOrNamespaceVariable
5488 }
5589
5690 override predicate isAdditionalFlowStep ( DataFlow:: Node n1 , DataFlow:: Node n2 ) {
5791 instructionTaintStep ( n1 .asInstruction ( ) , n2 .asInstruction ( ) )
5892 or
59- exists ( StoreInstruction i1 , LoadInstruction i2 , GlobalOrNamespaceVariable gv |
60- writesVariable ( i1 , gv ) and
61- readsVariable ( i2 , gv ) and
62- i1 = n1 .asInstruction ( ) and
63- i2 = n2 .asInstruction ( )
64- )
93+ writesVariable ( n1 .asInstruction ( ) , n2 .asVariable ( ) .( GlobalOrNamespaceVariable ) )
94+ or
95+ readsVariable ( n2 .asInstruction ( ) , n1 .asVariable ( ) .( GlobalOrNamespaceVariable ) )
6596 }
6697
6798 override predicate isBarrier ( DataFlow:: Node node ) { nodeIsBarrier ( node ) }
@@ -71,19 +102,20 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
71102 FromGlobalVarTaintTrackingCfg ( ) { this = "FromGlobalVarTaintTrackingCfg" }
72103
73104 override predicate isSource ( DataFlow:: Node source ) {
74- exists (
75- ToGlobalVarTaintTrackingCfg other , DataFlow:: Node prevSink , GlobalOrNamespaceVariable gv
76- |
77- other .hasFlowTo ( prevSink ) and
78- writesVariable ( prevSink .asInstruction ( ) , gv ) and
79- readsVariable ( source .asInstruction ( ) , gv )
80- )
105+ // This set of sources should be reasonably small, which is good for
106+ // performance since the set of sinks is very large.
107+ exists ( ToGlobalVarTaintTrackingCfg otherCfg | otherCfg .hasFlowTo ( source ) )
81108 }
82109
83- override predicate isSink ( DataFlow:: Node sink ) { any ( ) }
110+ override predicate isSink ( DataFlow:: Node sink ) { exists ( adjustedSink ( sink ) ) }
84111
85112 override predicate isAdditionalFlowStep ( DataFlow:: Node n1 , DataFlow:: Node n2 ) {
86113 instructionTaintStep ( n1 .asInstruction ( ) , n2 .asInstruction ( ) )
114+ or
115+ // Additional step for flow out of variables. There is no flow _into_
116+ // variables in this configuration, so this step only serves to take flow
117+ // out of a variable that's a source.
118+ readsVariable ( n2 .asInstruction ( ) , n1 .asVariable ( ) )
87119 }
88120
89121 override predicate isBarrier ( DataFlow:: Node node ) { nodeIsBarrier ( node ) }
@@ -123,15 +155,16 @@ private predicate nodeIsBarrier(DataFlow::Node node) {
123155
124156private predicate instructionTaintStep ( Instruction i1 , Instruction i2 ) {
125157 // Expressions computed from tainted data are also tainted
126- i2 =
127- any ( CallInstruction call |
128- isPureFunction ( call .getStaticCallTarget ( ) .getName ( ) ) and
129- call .getAnArgument ( ) = i1 and
130- forall ( Instruction arg | arg = call .getAnArgument ( ) | arg = i1 or predictableInstruction ( arg ) ) and
131- // flow through `strlen` tends to cause dubious results, if the length is
132- // bounded.
133- not call .getStaticCallTarget ( ) .getName ( ) = "strlen"
134- )
158+ exists ( CallInstruction call , int argIndex | call = i2 |
159+ isPureFunction ( call .getStaticCallTarget ( ) .getName ( ) ) and
160+ i1 = getACallArgumentOrIndirection ( call , argIndex ) and
161+ forall ( Instruction arg | arg = call .getAnArgument ( ) |
162+ arg = getACallArgumentOrIndirection ( call , argIndex ) or predictableInstruction ( arg )
163+ ) and
164+ // flow through `strlen` tends to cause dubious results, if the length is
165+ // bounded.
166+ not call .getStaticCallTarget ( ) .getName ( ) = "strlen"
167+ )
135168 or
136169 // Flow through pointer dereference
137170 i2 .( LoadInstruction ) .getSourceAddress ( ) = i1
@@ -172,7 +205,8 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
172205 any ( CallInstruction call |
173206 exists ( int indexIn |
174207 modelTaintToReturnValue ( call .getStaticCallTarget ( ) , indexIn ) and
175- i1 = getACallArgumentOrIndirection ( call , indexIn )
208+ i1 = getACallArgumentOrIndirection ( call , indexIn ) and
209+ not predictableOnlyFlow ( call .getStaticCallTarget ( ) .getName ( ) )
176210 )
177211 )
178212 or
@@ -315,23 +349,12 @@ predicate taintedIncludingGlobalVars(Expr source, Element tainted, string global
315349 globalVar = ""
316350 or
317351 exists (
318- ToGlobalVarTaintTrackingCfg toCfg , FromGlobalVarTaintTrackingCfg fromCfg , DataFlow :: Node store ,
319- GlobalOrNamespaceVariable global , DataFlow:: Node load , DataFlow:: Node sink
352+ ToGlobalVarTaintTrackingCfg toCfg , FromGlobalVarTaintTrackingCfg fromCfg ,
353+ DataFlow:: VariableNode variableNode , GlobalOrNamespaceVariable global , DataFlow:: Node sink
320354 |
321- toCfg .hasFlow ( getNodeForSource ( source ) , store ) and
322- store
323- .asInstruction ( )
324- .( StoreInstruction )
325- .getDestinationAddress ( )
326- .( VariableAddressInstruction )
327- .getASTVariable ( ) = global and
328- load
329- .asInstruction ( )
330- .( LoadInstruction )
331- .getSourceAddress ( )
332- .( VariableAddressInstruction )
333- .getASTVariable ( ) = global and
334- fromCfg .hasFlow ( load , sink ) and
355+ global = variableNode .getVariable ( ) and
356+ toCfg .hasFlow ( getNodeForSource ( source ) , variableNode ) and
357+ fromCfg .hasFlow ( variableNode , sink ) and
335358 tainted = adjustedSink ( sink ) and
336359 global = globalVarFromId ( globalVar )
337360 )
0 commit comments