Skip to content

Commit 984194d

Browse files
authored
Merge pull request #4406 from geoffw0/set
C++: Models for std::set and std::unordered_set
2 parents 4e116ba + 4db964f commit 984194d

File tree

9 files changed

+1544
-0
lines changed

9 files changed

+1544
-0
lines changed

cpp/ql/src/semmle/code/cpp/models/Models.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ private import implementations.Strftime
1717
private import implementations.StdContainer
1818
private import implementations.StdPair
1919
private import implementations.StdMap
20+
private import implementations.StdSet
2021
private import implementations.StdString
2122
private import implementations.Swap
2223
private import implementations.GetDelim

cpp/ql/src/semmle/code/cpp/models/implementations/StdMap.qll

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,33 @@
55
import semmle.code.cpp.models.interfaces.Taint
66
import semmle.code.cpp.models.implementations.Iterator
77

8+
/**
9+
* Additional model for map constructors using iterator inputs.
10+
*/
11+
class StdMapConstructor extends Constructor, TaintFunction {
12+
StdMapConstructor() {
13+
this.hasQualifiedName("std", "map", "map") or
14+
this.hasQualifiedName("std", "unordered_map", "unordered_map")
15+
}
16+
17+
/**
18+
* Gets the index of a parameter to this function that is an iterator.
19+
*/
20+
int getAnIteratorParameterIndex() {
21+
getParameter(result).getUnspecifiedType() instanceof Iterator
22+
}
23+
24+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
25+
// taint flow from any parameter of an iterator type to the qualifier
26+
input.isParameterDeref(getAnIteratorParameterIndex()) and
27+
(
28+
output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
29+
or
30+
output.isQualifierObject()
31+
)
32+
}
33+
}
34+
835
/**
936
* The standard map `insert` and `insert_or_assign` functions.
1037
*/
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* Provides models for C++ containers `std::set` and `std::unordered_set`.
3+
*/
4+
5+
import semmle.code.cpp.models.interfaces.Taint
6+
import semmle.code.cpp.models.implementations.Iterator
7+
8+
/**
9+
* Additional model for set constructors using iterator inputs.
10+
*/
11+
class StdSetConstructor extends Constructor, TaintFunction {
12+
StdSetConstructor() {
13+
this.hasQualifiedName("std", "set", "set") or
14+
this.hasQualifiedName("std", "unordered_set", "unordered_set")
15+
}
16+
17+
/**
18+
* Gets the index of a parameter to this function that is an iterator.
19+
*/
20+
int getAnIteratorParameterIndex() {
21+
getParameter(result).getUnspecifiedType() instanceof Iterator
22+
}
23+
24+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
25+
// taint flow from any parameter of an iterator type to the qualifier
26+
input.isParameterDeref(getAnIteratorParameterIndex()) and
27+
(
28+
output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
29+
or
30+
output.isQualifierObject()
31+
)
32+
}
33+
}
34+
35+
/**
36+
* The standard set `insert` and `insert_or_assign` functions.
37+
*/
38+
class StdSetInsert extends TaintFunction {
39+
StdSetInsert() { this.hasQualifiedName("std", ["set", "unordered_set"], "insert") }
40+
41+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
42+
// flow from last parameter to qualifier and return value
43+
// (where the return value is a pair, this should really flow just to the first part of it)
44+
input.isParameterDeref(getNumberOfParameters() - 1) and
45+
(
46+
output.isQualifierObject() or
47+
output.isReturnValue()
48+
)
49+
}
50+
}
51+
52+
/**
53+
* The standard set `swap` functions.
54+
*/
55+
class StdSetSwap extends TaintFunction {
56+
StdSetSwap() { this.hasQualifiedName("std", ["set", "unordered_set"], "swap") }
57+
58+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
59+
// container1.swap(container2)
60+
input.isQualifierObject() and
61+
output.isParameterDeref(0)
62+
or
63+
input.isParameterDeref(0) and
64+
output.isQualifierObject()
65+
}
66+
}
67+
68+
/**
69+
* The standard set `find` function.
70+
*/
71+
class StdSetFind extends TaintFunction {
72+
StdSetFind() { this.hasQualifiedName("std", ["set", "unordered_set"], "find") }
73+
74+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
75+
input.isQualifierObject() and
76+
output.isReturnValue()
77+
}
78+
}
79+
80+
/**
81+
* The standard set `erase` function.
82+
*/
83+
class StdSetErase extends TaintFunction {
84+
StdSetErase() { this.hasQualifiedName("std", ["set", "unordered_set"], "erase") }
85+
86+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
87+
// flow from qualifier to iterator return value
88+
getType().getUnderlyingType() instanceof Iterator and
89+
input.isQualifierObject() and
90+
output.isReturnValue()
91+
}
92+
}

cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected

Lines changed: 937 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
2+
#include "stl.h"
3+
4+
using namespace std;
5+
6+
char *source();
7+
8+
void sink(char *);
9+
void sink(std::set<char *>);
10+
void sink(std::set<char *>::iterator);
11+
void sink(std::unordered_set<char *>);
12+
void sink(std::unordered_set<char *>::iterator);
13+
14+
void test_set()
15+
{
16+
// insert, find
17+
std::set<char *> s1, s2, s3, s4, s5, s6;
18+
19+
sink(s1.insert("abc").first);
20+
sink(s2.insert(source()).first); // tainted
21+
sink(s3.insert(s3.begin(), "abc"));
22+
sink(s4.insert(s4.begin(), source())); // tainted
23+
s5.insert(s1.begin(), s1.end());
24+
s6.insert(s2.begin(), s2.end());
25+
sink(s1);
26+
sink(s2); // tainted
27+
sink(s3);
28+
sink(s4); // tainted
29+
sink(s5);
30+
sink(s6); // tainted
31+
sink(s1.find("abc"));
32+
sink(s2.find("abc")); // tainted
33+
sink(s3.find("abc"));
34+
sink(s4.find("abc")); // tainted
35+
sink(s5.find("abc"));
36+
sink(s6.find("abc")); // tainted
37+
38+
// copy constructors and assignment
39+
std::set<char *> s7(s2);
40+
std::set<char *> s8 = s2;
41+
std::set<char *> s9(s2.begin(), s2.end());
42+
std::set<char *> s10;
43+
s10 = s2;
44+
sink(s7); // tainted
45+
sink(s8); // tainted
46+
sink(s9); // tainted
47+
sink(s10); // tainted
48+
sink(s7.find("abc")); // tainted
49+
sink(s8.find("abc")); // tainted
50+
sink(s9.find("abc")); // tainted
51+
sink(s10.find("abc")); // tainted
52+
53+
// iterators
54+
std::set<char *>::iterator i1, i2;
55+
for (i1 = s1.begin(); i1 != s1.end(); i1++)
56+
{
57+
sink(*i1);
58+
}
59+
for (i2 = s2.begin(); i2 != s2.end(); i2++)
60+
{
61+
sink(*i2); // tainted
62+
}
63+
64+
// ranges
65+
std::set<char *> s11;
66+
s11.insert("a");
67+
s11.insert(source());
68+
s11.insert("c");
69+
sink(s11.lower_bound("b")); // tainted [NOT DETECTED]
70+
sink(s11.upper_bound("b")); // tainted [NOT DETECTED]
71+
sink(s11.equal_range("b").first); // tainted [NOT DETECTED]
72+
sink(s11.equal_range("b").second); // tainted [NOT DETECTED]
73+
74+
// swap
75+
std::set<char *> s12, s13, s14, s15;
76+
s12.insert(source());
77+
s15.insert(source());
78+
sink(s12); // tainted
79+
sink(s13);
80+
sink(s14);
81+
sink(s15); // tainted
82+
s12.swap(s13);
83+
s14.swap(s15);
84+
sink(s12); // [FALSE POSITIVE]
85+
sink(s13); // tainted
86+
sink(s14); // tainted
87+
sink(s15); // [FALSE POSITIVE]
88+
89+
// merge
90+
std::set<char *> s16, s17, s18, s19;
91+
s16.insert(source());
92+
s17.insert("abc");
93+
s18.insert("def");
94+
s19.insert(source());
95+
sink(s16); // tainted
96+
sink(s17);
97+
sink(s18);
98+
sink(s19); // tainted
99+
s16.merge(s17);
100+
s18.merge(s19);
101+
sink(s16); // tainted
102+
sink(s17); // tainted [NOT DETECTED]
103+
sink(s18); // tainted [NOT DETECTED]
104+
sink(s19); // tainted
105+
106+
// erase, clear
107+
std::set<char *> s20;
108+
s20.insert(source());
109+
s20.insert(source());
110+
sink(s20); // tainted
111+
sink(s20.erase(s20.begin())); // tainted
112+
sink(s20); // tainted
113+
s20.clear();
114+
sink(s20); // [FALSE POSITIVE]
115+
116+
// emplace, emplace_hint
117+
std::set<char *> s21, s22;
118+
sink(s21.emplace("abc").first);
119+
sink(s21);
120+
sink(s21.emplace(source()).first); // tainted [NOT DETECTED]
121+
sink(s21); // tainted [NOT DETECTED]
122+
sink(s22.emplace_hint(s22.begin(), "abc"));
123+
sink(s22);
124+
sink(s22.emplace_hint(s22.begin(), source())); // tainted [NOT DETECTED]
125+
sink(s22); // tainted [NOT DETECTED]
126+
}
127+
128+
void test_unordered_set()
129+
{
130+
// insert, find
131+
std::unordered_set<char *> s1, s2, s3, s4, s5, s6;
132+
133+
sink(s1.insert("abc").first);
134+
sink(s2.insert(source()).first); // tainted
135+
sink(s3.insert(s3.begin(), "abc"));
136+
sink(s4.insert(s4.begin(), source())); // tainted
137+
s5.insert(s1.begin(), s1.end());
138+
s6.insert(s2.begin(), s2.end());
139+
sink(s1);
140+
sink(s2); // tainted
141+
sink(s3);
142+
sink(s4); // tainted
143+
sink(s5);
144+
sink(s6); // tainted
145+
sink(s1.find("abc"));
146+
sink(s2.find("abc")); // tainted
147+
sink(s3.find("abc"));
148+
sink(s4.find("abc")); // tainted
149+
sink(s5.find("abc"));
150+
sink(s6.find("abc")); // tainted
151+
152+
// copy constructors and assignment
153+
std::unordered_set<char *> s7(s2);
154+
std::unordered_set<char *> s8 = s2;
155+
std::unordered_set<char *> s9(s2.begin(), s2.end());
156+
std::unordered_set<char *> s10;
157+
s10 = s2;
158+
sink(s7); // tainted
159+
sink(s8); // tainted
160+
sink(s9); // tainted
161+
sink(s10); // tainted
162+
sink(s7.find("abc")); // tainted
163+
sink(s8.find("abc")); // tainted
164+
sink(s9.find("abc")); // tainted
165+
sink(s10.find("abc")); // tainted
166+
167+
// iterators
168+
std::unordered_set<char *>::iterator i1, i2;
169+
for (i1 = s1.begin(); i1 != s1.end(); i1++)
170+
{
171+
sink(*i1);
172+
}
173+
for (i2 = s2.begin(); i2 != s2.end(); i2++)
174+
{
175+
sink(*i2); // tainted
176+
}
177+
178+
// ranges
179+
std::unordered_set<char *> s11;
180+
s11.insert("a");
181+
s11.insert(source());
182+
s11.insert("c");
183+
sink(s11.equal_range("b").first); // tainted [NOT DETECTED]
184+
sink(s11.equal_range("b").second); // tainted [NOT DETECTED]
185+
186+
// swap
187+
std::unordered_set<char *> s12, s13, s14, s15;
188+
s12.insert(source());
189+
s15.insert(source());
190+
sink(s12); // tainted
191+
sink(s13);
192+
sink(s14);
193+
sink(s15); // tainted
194+
s12.swap(s13);
195+
s14.swap(s15);
196+
sink(s12); // [FALSE POSITIVE]
197+
sink(s13); // tainted
198+
sink(s14); // tainted
199+
sink(s15); // [FALSE POSITIVE]
200+
201+
// merge
202+
std::unordered_set<char *> s16, s17, s18, s19;
203+
s16.insert(source());
204+
s17.insert("abc");
205+
s18.insert("def");
206+
s19.insert(source());
207+
sink(s16); // tainted
208+
sink(s17);
209+
sink(s18);
210+
sink(s19); // tainted
211+
s16.merge(s17);
212+
s18.merge(s19);
213+
sink(s16); // tainted
214+
sink(s17); // tainted [NOT DETECTED]
215+
sink(s18); // tainted [NOT DETECTED]
216+
sink(s19); // tainted
217+
218+
// erase, clear
219+
std::unordered_set<char *> s20;
220+
s20.insert(source());
221+
s20.insert(source());
222+
sink(s20); // tainted
223+
sink(s20.erase(s20.begin())); // tainted
224+
sink(s20); // tainted
225+
s20.clear();
226+
sink(s20); // [FALSE POSITIVE]
227+
228+
// emplace, emplace_hint
229+
std::unordered_set<char *> s21, s22;
230+
sink(s21.emplace("abc").first);
231+
sink(s21);
232+
sink(s21.emplace(source()).first); // tainted [NOT DETECTED]
233+
sink(s21); // tainted [NOT DETECTED]
234+
sink(s22.emplace_hint(s22.begin(), "abc"));
235+
sink(s22);
236+
sink(s22.emplace_hint(s22.begin(), source())); // tainted [NOT DETECTED]
237+
sink(s22); // tainted [NOT DETECTED]
238+
}

0 commit comments

Comments
 (0)