-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathFormattingFunction.qll
More file actions
194 lines (171 loc) · 6.31 KB
/
FormattingFunction.qll
File metadata and controls
194 lines (171 loc) · 6.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/**
* Provides a class for modeling `printf`-style formatting functions. To use
* this QL library, create a QL class extending `FormattingFunction` with a
* characteristic predicate that selects the function or set of functions you
* are modeling. Within that class, override the predicates provided by
* `FormattingFunction` to match the flow within that function.
*/
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Taint
pragma[nomagic]
private Type stripTopLevelSpecifiersOnly(Type t) {
result = stripTopLevelSpecifiersOnly(pragma[only_bind_out](t.(SpecifiedType).getBaseType()))
or
result = t and
not t instanceof SpecifiedType
}
/**
* A type that is used as a format string by any formatting function.
*/
Type getAFormatterWideType() {
exists(FormattingFunction ff |
result = stripTopLevelSpecifiersOnly(ff.getFormatCharType()) and
result.getSize() != 1
)
}
/**
* A type that is used as a format string by any formatting function, or `wchar_t` if
* there is none.
*/
private Type getAFormatterWideTypeOrDefault() {
result = getAFormatterWideType()
or
not exists(getAFormatterWideType()) and
result instanceof Wchar_t
}
/**
* A standard library function that uses a `printf`-like formatting string.
*/
abstract class FormattingFunction extends ArrayFunction, TaintFunction {
/** Gets the position at which the format parameter occurs. */
abstract int getFormatParameterIndex();
override string getAPrimaryQlClass() { result = "FormattingFunction" }
/**
* Holds if this `FormattingFunction` is in a context that supports
* Microsoft rules and extensions.
*/
predicate isMicrosoft() { anyFileCompiledAsMicrosoft() }
/**
* Gets the character type used in the format string for this function.
*/
Type getFormatCharType() {
result =
stripTopLevelSpecifiersOnly(stripTopLevelSpecifiersOnly(this.getParameter(this.getFormatParameterIndex())
.getType()
.getUnderlyingType()).(PointerType).getBaseType())
}
/**
* Gets the default character type expected for `%s` by this function. Typically
* `char` or `wchar_t`.
*/
Type getDefaultCharType() {
this.isMicrosoft() and
result = this.getFormatCharType()
or
not this.isMicrosoft() and
result instanceof PlainCharType
}
/**
* Gets the non-default character type expected for `%S` by this function. Typically
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell
* which is correct for a particular function.
*/
Type getNonDefaultCharType() {
this.getDefaultCharType().getSize() = 1 and
result = this.getWideCharType()
or
not this.getDefaultCharType().getSize() = 1 and
result instanceof PlainCharType
}
/**
* Gets the wide character type for this function. This is usually `wchar_t`. On some
* snapshots there may be multiple results where we can't tell which is correct for a
* particular function.
*/
pragma[nomagic]
Type getWideCharType() {
result = this.getFormatCharType() and
result.getSize() > 1
or
not this.getFormatCharType().getSize() > 1 and
result = getAFormatterWideTypeOrDefault() // may have more than one result
}
/**
* Gets the position at which the output parameter, if any, occurs. If
* `isStream` is `true`, the output parameter is a stream (that is, this
* function behaves like `fprintf`). If `isStream` is `false`, the output
* parameter is a buffer (that is, this function behaves like `sprintf`).
*/
int getOutputParameterIndex(boolean isStream) { none() }
/**
* Holds if this function outputs to a global stream such as standard output,
* standard error or a system log. For example `printf`.
*/
predicate isOutputGlobal() { none() }
/**
* Gets the position of the first format argument, corresponding with
* the first format specifier in the format string. We ignore all
* implicit function definitions.
*
* There is no result if the formatting function takes a `va_list` argument.
*/
int getFirstFormatArgumentIndex() {
result > 0 and
if this.hasDefinition()
then result = this.getDefinition().getNumberOfParameters()
else
if this instanceof BuiltInFunction
then result = this.getNumberOfParameters()
else
forex(FunctionDeclarationEntry fde | fde = this.getAnExplicitDeclarationEntry() |
result = fde.getNumberOfParameters()
)
}
/**
* Gets the position of the buffer size argument, if any.
*/
int getSizeParameterIndex() { none() }
override predicate hasArrayWithNullTerminator(int bufParam) {
bufParam = this.getFormatParameterIndex()
}
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
bufParam = this.getOutputParameterIndex(false) and
countParam = this.getSizeParameterIndex()
}
override predicate hasArrayWithUnknownSize(int bufParam) {
bufParam = this.getOutputParameterIndex(false) and
not exists(this.getSizeParameterIndex())
}
override predicate hasArrayInput(int bufParam) { bufParam = this.getFormatParameterIndex() }
override predicate hasArrayOutput(int bufParam) { bufParam = this.getOutputParameterIndex(false) }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
exists(int arg |
arg = this.getFormatParameterIndex() or
arg >= this.getFirstFormatArgumentIndex()
|
(input.isParameterDeref(arg) or input.isParameter(arg)) and
output.isParameterDeref(this.getOutputParameterIndex(_))
)
}
final override predicate isPartialWrite(FunctionOutput output) {
exists(int outputParameterIndex |
output.isParameterDeref(outputParameterIndex) and
// We require the output to be a stream since that definitely means that
// it's a partial write. If it's not a stream then it will most likely
// fill the whole buffer.
outputParameterIndex = this.getOutputParameterIndex(true)
)
}
}
/**
* The standard functions `snprintf` and `swprintf`, and their
* Microsoft and glib variants.
*/
abstract class Snprintf extends FormattingFunction {
/**
* Holds if this function returns the length of the formatted string
* that would have been output, regardless of the amount of space
* in the buffer.
*/
predicate returnsFullFormatLength() { none() }
}