1
1
/**
2
2
* @name Non-constant format string
3
- * @description Passing a non-constant 'format' string to a printf-like function can lead
3
+ * @description Passing a value that is not a string literal 'format' string to a printf-like function can lead
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
15
* @kind problem
8
16
* @problem.severity recommendation
9
17
* @security-severity 9.3
@@ -32,40 +40,37 @@ class UncalledFunction extends Function {
32
40
}
33
41
}
34
42
35
- /*
36
- * const char* means (const char)*, so the pointer is not const, the pointed to value is.
37
- * Grabs the base type of the underlying type of `t` if `t` is a pointer and checks `isConst()` else
38
- * checks on the underlying type of `t` alone.
39
- */
40
- predicate hasConstSpecifier ( Type t ) {
41
- if t .getUnderlyingType ( ) instanceof PointerType
42
- then t .getUnderlyingType ( ) .( PointerType ) .getBaseType ( ) .isConst ( )
43
- else t .getUnderlyingType ( ) .isConst ( )
44
- }
45
-
46
43
/**
47
- * Holds if `node` is a non-constant source of data flow.
44
+ * Holds if `node` is a non-constant source of data flow for non-const format string detection .
48
45
* This is defined as either:
49
46
* 1) a `FlowSource`
50
47
* 2) a parameter of an 'uncalled' function
51
48
* 3) an argument to a function with no definition that is not known to define the output through its input
52
49
* 4) an out arg of a function with no definition that is not known to define the output through its input
53
50
*
54
- * With exception to `FlowSource` all non-const values have a type that is not const
55
- * (declared without a `const` specifier)
56
- * ASSUMPTION: any const values are assumed to be static if their assignment is not seen
57
- * i.e., assuming users did not get non-const data and cast into a const
58
- *
59
51
* The latter two cases address identifying standard string manipulation libraries as input sources
60
- * e.g., strcpy, but it will identify unknown function calls as possible non-constant source
61
- * since it cannot be determined if the out argument or return is constant.
52
+ * e.g., strcpy. More simply, functions without definitions that are known to manipulate the
53
+ * input to produce an output are not sources. Instead the ultimate source of input to these functions
54
+ * should be considered as the source.
55
+ *
56
+ * False Negative Implication: This approach has false negatives (fails to identify non-const sources)
57
+ * when the source is a field of a struct or object and the initialization is not observed statically.
58
+ * There are 3 general cases where this can occur:
59
+ * 1) Parameters of uncalled functions that are structs/objects and a field is accessed for a format string.
60
+ * 2) A local variable that is a struct/object and initialization of the field occurs in code that is unseen statically.
61
+ * e.g., an object constructor isn't known statically, or a function sets fields
62
+ * of a struct, but the function is not known statically.
63
+ * 3) A function meeting cases (3) and (4) above returns (through an out argument or return value)
64
+ * a struct or object where a field containing a format string has been initialized.
65
+ *
66
+ * Note, uninitialized variables used as format strings are never detected by design.
67
+ * Uninitialized variables are a separate vulnerability concern and should be addressed by a separate query.
62
68
*/
63
69
predicate isNonConst ( DataFlow:: Node node ) {
64
70
node instanceof FlowSource
65
71
or
66
72
// Parameters of uncalled functions that aren't const
67
73
exists ( UncalledFunction f , Parameter p |
68
- //not hasConstSpecifier(p.getType()) and
69
74
f .getAParameter ( ) = p and
70
75
p = node .asParameter ( )
71
76
)
@@ -77,23 +82,20 @@ predicate isNonConst(DataFlow::Node node) {
77
82
// are considered as possible non-const sources
78
83
// The function's output must also not be const to be considered a non-const source
79
84
exists ( Call c |
80
- exists ( Expr arg | c .getAnArgument ( ) = arg |
81
- arg = node .asDefiningArgument ( )
82
- // and
83
- // not hasConstSpecifier(arg.getType())
84
- )
85
+ exists ( Expr arg | c .getAnArgument ( ) = arg | arg = node .asDefiningArgument ( ) )
85
86
or
86
- c = node .asIndirectExpr ( )
87
- // and not hasConstSpecifier(c.getType())
87
+ c = node .asIndirectExpr ( )
88
88
) and
89
89
not exists ( FunctionInput input , FunctionOutput output , CallInstruction call |
90
90
// NOTE: we must include dataflow and taintflow. e.g., including only dataflow we will find sprintf
91
91
// variant function's output are now possible non-const sources
92
92
(
93
- pragma [ only_bind_out ] ( call .getStaticCallTarget ( ) ) .( DataFlowFunction ) .hasDataFlow ( input , output ) or
93
+ pragma [ only_bind_out ] ( call .getStaticCallTarget ( ) )
94
+ .( DataFlowFunction )
95
+ .hasDataFlow ( input , output ) or
94
96
pragma [ only_bind_out ] ( call .getStaticCallTarget ( ) ) .( TaintFunction ) .hasTaintFlow ( input , output )
95
97
) and
96
- node = callOutput ( call , output )
98
+ node = callOutput ( call , output )
97
99
) and
98
100
not exists ( Call c |
99
101
c .getTarget ( ) .hasDefinition ( ) and
0 commit comments