-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathFileWrite.qll
More file actions
180 lines (158 loc) · 5.09 KB
/
FileWrite.qll
File metadata and controls
180 lines (158 loc) · 5.09 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
/**
* Provides classes for modeling writing of data to files through various standard mechanisms such as `fprintf`, `fwrite` and `operator<<`.
*/
import cpp
/**
* A function call that writes to a file.
*/
class FileWrite extends Expr {
FileWrite() { fileWrite(this, _, _) }
/**
* Gets a source expression of this write.
*/
Expr getASource() { fileWrite(this, result, _) }
/**
* Gets the expression for the object being written to.
*/
Expr getDest() { fileWrite(this, _, result) }
/**
* Gets the conversion character from `source` for this write, if it exists and is known.
* For example in the following code the write of `value1` has conversion character `"s"`, whereas
* the write of `value2` has no conversion specifier.
* ```
* fprintf(file, "%s", value1);
* stream << value2;
* ```
*/
string getSourceConvChar(Expr source) { fileWriteWithConvChar(this, source, result) }
}
/**
* A `std::basic_ostream` class, or something that can be used
* as one.
*/
class BasicOStreamClass extends Type {
BasicOStreamClass() {
this.(Class).getName().matches("basic\\_ostream%")
or
this.getUnspecifiedType() instanceof BasicOStreamClass
or
this.(Class).getABaseClass() instanceof BasicOStreamClass
or
this.(ReferenceType).getBaseType() instanceof BasicOStreamClass
}
}
/**
* A call to a member of `std::basic_ostream`, or something related,
* or a call with one of those objects as the first parameter.
*/
class BasicOStreamCall extends FunctionCall {
BasicOStreamCall() {
if this.getTarget() instanceof MemberFunction
then this.getQualifier().getType() instanceof BasicOStreamClass
else this.getArgument(0).getType() instanceof BasicOStreamClass
}
}
/**
* Output by a function that can be chained, such as `operator<<`.
*/
abstract class ChainedOutputCall extends BasicOStreamCall {
/**
* Gets the source expression of this output.
*/
abstract Expr getSource();
/**
* Gets the immediate destination expression of this output.
*/
abstract Expr getDest();
/**
* Gets the destination at the far left-hand end of the output chain.
*/
Expr getEndDest() {
// recurse into the destination
result = this.getDest().(ChainedOutputCall).getEndDest()
or
// or return something other than a ChainedOutputCall
result = this.getDest() and
not result instanceof ChainedOutputCall
}
}
/**
* A call to `operator<<` on an output stream.
*/
class OperatorLShiftCall extends ChainedOutputCall {
OperatorLShiftCall() { this.getTarget().(Operator).hasName("operator<<") }
override Expr getSource() {
if this.getTarget() instanceof MemberFunction
then result = this.getArgument(0)
else result = this.getArgument(1)
}
override Expr getDest() {
if this.getTarget() instanceof MemberFunction
then result = this.getQualifier()
else result = this.getArgument(0)
}
}
/**
* A call to 'put'.
*/
class PutFunctionCall extends ChainedOutputCall {
PutFunctionCall() { this.getTarget().(MemberFunction).hasName("put") }
override Expr getSource() { result = this.getArgument(0) }
override Expr getDest() { result = this.getQualifier() }
}
/**
* A call to 'write'.
*/
class WriteFunctionCall extends ChainedOutputCall {
WriteFunctionCall() { this.getTarget().(MemberFunction).hasName("write") }
override Expr getSource() { result = this.getArgument(0) }
override Expr getDest() { result = this.getQualifier() }
}
/**
* Whether the function call is a call to `operator<<` or a similar function, that eventually starts at the given file stream.
*/
private predicate fileStreamChain(ChainedOutputCall out, Expr source, Expr dest) {
source = out.getSource() and
dest = out.getEndDest() and
dest.getUnderlyingType().(Class).getSimpleName() = ["basic_ofstream", "basic_fstream"]
}
/**
* Whether the function call is a write to file 'dest' from 'source'.
*/
private predicate fileWrite(Call write, Expr source, Expr dest) {
exists(Function f, int s, int d |
f = write.getTarget() and source = write.getArgument(s) and dest = write.getArgument(d)
|
exists(string name | f.hasGlobalOrStdName(name) |
// named functions
name = "fwrite" and s = 0 and d = 3
or
name = ["fputs", "fputws", "fputc", "fputwc", "putc", "putwc", "putw"] and
s = 0 and
d = 1
)
or
// fprintf
s >= f.(FormattingFunction).getFormatParameterIndex() and
d = f.(FormattingFunction).getOutputParameterIndex(true)
)
or
// file stream using '<<', 'put' or 'write'
fileStreamChain(write, source, dest)
}
/**
* Whether the function call is a write to a file from 'source' with
* conversion character 'conv'. Does not hold if there isn't a conversion
* character, or if it is unknown (for example the format string is not a
* constant).
*/
private predicate fileWriteWithConvChar(FormattingFunctionCall ffc, Expr source, string conv) {
// fprintf
exists(FormattingFunction f, int n |
f = ffc.getTarget() and
source = ffc.getFormatArgument(n)
|
exists(f.getOutputParameterIndex(true)) and
conv = ffc.getFormat().(FormatLiteral).getConversionChar(n)
)
}