4
4
* to a mismatch between the number of arguments defined by the 'format' and the number
5
5
* of arguments actually passed to the function. If the format string ultimately stems
6
6
* from an untrusted source, this can be used for exploits.
7
- * This query finds all sources leading to a format string that cannot be verified to be literal.
8
- * Even if the format string type is `const char*` it is still considered non-constant if the
9
- * value is not a string literal. For example, a parameter to a function that is never observed to be called
10
- * that takes in a `const char*` and uses it as a format string, there is no way to verify the originating
11
- * value was a string literal. This is especially problematic with conversion of c strings to char *,
12
- * via `c_str()`, which returns a `const char*`, regardless if the original string was a string literal or not.
13
- * The query does not consider uninitialized variables as non-constant sources. Uninitialized
14
- * variables are a separate vulnerability concern and should be addressed by a separate query.
7
+ * This query finds format strings coming from non-literal sources. Note that format strings of
8
+ * type `const char*` it is still considered non-constant if the value is not coming from a string
9
+ * literal. For example, for a parameter with type `const char*` of an exported function that is
10
+ * used as a format string, there is no way to ensure the originating value was a string literal.
15
11
* @kind path-problem
16
12
* @problem.severity recommendation
17
13
* @security-severity 9.3
@@ -30,7 +26,6 @@ import semmle.code.cpp.ir.dataflow.internal.ModelUtil
30
26
import semmle.code.cpp.models.interfaces.DataFlow
31
27
import semmle.code.cpp.models.interfaces.Taint
32
28
import semmle.code.cpp.ir.IR
33
- import NonConstFlow:: PathGraph
34
29
35
30
class UncalledFunction extends Function {
36
31
UncalledFunction ( ) {
@@ -73,11 +68,7 @@ predicate isNonConst(DataFlow::Node node) {
73
68
// Parameters of uncalled functions that aren't const
74
69
exists ( UncalledFunction f , Parameter p |
75
70
f .getAParameter ( ) = p and
76
- p = node .asParameter ( ) and
77
- // Exclude main in this instance since that should have its own defined FlowSource
78
- // and including main would likely result in redundancy.
79
- // Note, argc is not a flow source, so only filter out argv
80
- ( p .getFunction ( ) .getName ( ) = "main" implies not p .getName ( ) = "argv" )
71
+ p = node .asParameter ( )
81
72
)
82
73
or
83
74
// Consider as an input any out arg of a function or a function's return where the function is not:
@@ -86,21 +77,25 @@ predicate isNonConst(DataFlow::Node node) {
86
77
// i.e., functions that with unknown bodies and are not known to define the output through its input
87
78
// are considered as possible non-const sources
88
79
// The function's output must also not be const to be considered a non-const source
89
- exists ( Call c |
90
- exists ( Expr arg | c .getAnArgument ( ) = arg | arg = node .asDefiningArgument ( ) )
80
+ exists ( Function func , CallInstruction call |
81
+ // NOTE: could use `Call` getAnArgument() instead of `CallInstruction` but requires two
82
+ // variables representing the same call in ordoer to use `callOutput` below.
83
+ exists ( Expr arg |
84
+ call .getPositionalArgumentOperand ( _) .getDef ( ) .getUnconvertedResultExpression ( ) = arg and
85
+ arg = node .asDefiningArgument ( )
86
+ )
91
87
or
92
- c = node .asIndirectExpr ( )
93
- ) and
94
- not exists ( FunctionInput input , FunctionOutput output , CallInstruction call |
95
- // NOTE: we must include dataflow and taintflow. e.g., including only dataflow we will find sprintf
96
- // variant function's output are now possible non-const sources
97
- (
98
- pragma [ only_bind_out ] ( call .getStaticCallTarget ( ) )
99
- .( DataFlowFunction )
100
- .hasDataFlow ( input , output ) or
101
- pragma [ only_bind_out ] ( call .getStaticCallTarget ( ) ) .( TaintFunction ) .hasTaintFlow ( input , output )
102
- ) and
103
- node = callOutput ( call , output )
88
+ call .getUnconvertedResultExpression ( ) = node .asIndirectExpr ( )
89
+ |
90
+ func = call .getStaticCallTarget ( ) and
91
+ not exists ( FunctionOutput output |
92
+ // NOTE: we must include dataflow and taintflow. e.g., including only dataflow we will find sprintf
93
+ // variant function's output are now possible non-const sources
94
+ pragma [ only_bind_out ] ( func ) .( DataFlowFunction ) .hasDataFlow ( _, output ) or
95
+ pragma [ only_bind_out ] ( func ) .( TaintFunction ) .hasTaintFlow ( _, output )
96
+ |
97
+ node = callOutput ( call , output )
98
+ )
104
99
) and
105
100
not exists ( Call c |
106
101
c .getTarget ( ) .hasDefinition ( ) and
@@ -132,13 +127,11 @@ module NonConstFlowConfig implements DataFlow::ConfigSig {
132
127
133
128
module NonConstFlow = TaintTracking:: Global< NonConstFlowConfig > ;
134
129
135
- from
136
- FormattingFunctionCall call , Expr formatString , NonConstFlow:: PathNode sink ,
137
- NonConstFlow:: PathNode source
130
+ from FormattingFunctionCall call , Expr formatString , DataFlow:: Node sink
138
131
where
139
- isSinkImpl ( sink .getNode ( ) , formatString ) and
140
132
call .getArgument ( call .getFormatParameterIndex ( ) ) = formatString and
141
- NonConstFlow:: flowPath ( source , sink )
142
- select sink .getNode ( ) , source , sink ,
143
- "The format string argument to $@ has a source which cannot be " +
144
- "verified to originate from a string literal." , call , call .getTarget ( ) .getName ( )
133
+ NonConstFlow:: flowTo ( sink ) and
134
+ isSinkImpl ( sink , formatString )
135
+ select formatString ,
136
+ "The format string argument to " + call .getTarget ( ) .getName ( ) +
137
+ " should be constant to prevent security issues and other potential errors."
0 commit comments