Skip to content

Commit 16c7a35

Browse files
authored
Merge pull request github#3195 from geoffw0/taintstring
C++: Model taint flow through std::string constructor and c_str()
2 parents dc774e0 + 73bfd81 commit 16c7a35

File tree

8 files changed

+195
-0
lines changed

8 files changed

+195
-0
lines changed

change-notes/1.24/analysis-cpp.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
4646
`StackVariableReachability`. The functionality is the same.
4747
* The models library models `strlen` in more detail, and includes common variations such as `wcslen`.
4848
* The models library models `gets` and similar functions.
49+
* The models library now partially models `std::string`.
4950
* The taint tracking library (`semmle.code.cpp.dataflow.TaintTracking`) has had
5051
the following improvements:
5152
* The library now models data flow through `strdup` and similar functions.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ private import implementations.Strcat
1212
private import implementations.Strcpy
1313
private import implementations.Strdup
1414
private import implementations.Strftime
15+
private import implementations.StdString
1516
private import implementations.Swap
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import semmle.code.cpp.models.interfaces.Taint
2+
3+
/**
4+
* The `std::basic_string` constructor(s).
5+
*/
6+
class StdStringConstructor extends TaintFunction {
7+
StdStringConstructor() { this.hasQualifiedName("std", "basic_string", "basic_string") }
8+
9+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
10+
// flow from any constructor argument to return value
11+
input.isParameter(_) and
12+
output.isReturnValue()
13+
}
14+
}
15+
16+
/**
17+
* The standard function `std::string.c_str`.
18+
*/
19+
class StdStringCStr extends TaintFunction {
20+
StdStringCStr() { this.hasQualifiedName("std", "basic_string", "c_str") }
21+
22+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
23+
// flow from string itself (qualifier) to return value
24+
input.isQualifierObject() and
25+
output.isReturnValue()
26+
}
27+
}

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,53 @@
105105
| format.cpp:130:23:130:23 | 0 | format.cpp:130:21:130:24 | {...} | TAINT |
106106
| format.cpp:131:39:131:45 | ref arg & ... | format.cpp:132:8:132:13 | buffer | |
107107
| format.cpp:131:40:131:45 | buffer | format.cpp:131:39:131:45 | & ... | |
108+
| stl.cpp:67:12:67:17 | call to source | stl.cpp:71:7:71:7 | a | |
109+
| stl.cpp:68:16:68:20 | 123 | stl.cpp:68:16:68:21 | call to basic_string | TAINT |
110+
| stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:72:7:72:7 | b | |
111+
| stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:74:7:74:7 | b | |
112+
| stl.cpp:69:16:69:21 | call to source | stl.cpp:69:16:69:24 | call to basic_string | TAINT |
113+
| stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:73:7:73:7 | c | |
114+
| stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:75:7:75:7 | c | |
115+
| stl.cpp:74:7:74:7 | b | stl.cpp:74:9:74:13 | call to c_str | TAINT |
116+
| stl.cpp:75:7:75:7 | c | stl.cpp:75:9:75:13 | call to c_str | TAINT |
117+
| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:83:2:83:4 | ss1 | |
118+
| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:89:7:89:9 | ss1 | |
119+
| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:94:7:94:9 | ss1 | |
120+
| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:84:2:84:4 | ss2 | |
121+
| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:90:7:90:9 | ss2 | |
122+
| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:95:7:95:9 | ss2 | |
123+
| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:85:2:85:4 | ss3 | |
124+
| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:91:7:91:9 | ss3 | |
125+
| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:96:7:96:9 | ss3 | |
126+
| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:86:2:86:4 | ss4 | |
127+
| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:92:7:92:9 | ss4 | |
128+
| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:97:7:97:9 | ss4 | |
129+
| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:87:2:87:4 | ss5 | |
130+
| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:93:7:93:9 | ss5 | |
131+
| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:98:7:98:9 | ss5 | |
132+
| stl.cpp:81:16:81:21 | call to source | stl.cpp:81:16:81:24 | call to basic_string | TAINT |
133+
| stl.cpp:81:16:81:24 | call to basic_string | stl.cpp:87:9:87:9 | t | |
134+
| stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:89:7:89:9 | ss1 | |
135+
| stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:94:7:94:9 | ss1 | |
136+
| stl.cpp:84:2:84:4 | ref arg ss2 | stl.cpp:90:7:90:9 | ss2 | |
137+
| stl.cpp:84:2:84:4 | ref arg ss2 | stl.cpp:95:7:95:9 | ss2 | |
138+
| stl.cpp:85:2:85:4 | ref arg ss3 | stl.cpp:91:7:91:9 | ss3 | |
139+
| stl.cpp:85:2:85:4 | ref arg ss3 | stl.cpp:96:7:96:9 | ss3 | |
140+
| stl.cpp:86:2:86:4 | ref arg ss4 | stl.cpp:92:7:92:9 | ss4 | |
141+
| stl.cpp:86:2:86:4 | ref arg ss4 | stl.cpp:97:7:97:9 | ss4 | |
142+
| stl.cpp:87:2:87:4 | ref arg ss5 | stl.cpp:93:7:93:9 | ss5 | |
143+
| stl.cpp:87:2:87:4 | ref arg ss5 | stl.cpp:98:7:98:9 | ss5 | |
144+
| stl.cpp:101:32:101:37 | source | stl.cpp:106:9:106:14 | source | |
145+
| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:105:2:105:4 | ss1 | |
146+
| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:108:7:108:9 | ss1 | |
147+
| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:110:7:110:9 | ss1 | |
148+
| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:106:2:106:4 | ss2 | |
149+
| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:109:7:109:9 | ss2 | |
150+
| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:111:7:111:9 | ss2 | |
151+
| stl.cpp:105:2:105:4 | ss1 [post update] | stl.cpp:108:7:108:9 | ss1 | |
152+
| stl.cpp:105:2:105:4 | ss1 [post update] | stl.cpp:110:7:110:9 | ss1 | |
153+
| stl.cpp:106:2:106:4 | ss2 [post update] | stl.cpp:109:7:109:9 | ss2 | |
154+
| stl.cpp:106:2:106:4 | ss2 [post update] | stl.cpp:111:7:111:9 | ss2 | |
108155
| taint.cpp:4:27:4:33 | source1 | taint.cpp:6:13:6:19 | source1 | |
109156
| taint.cpp:4:40:4:45 | clean1 | taint.cpp:5:8:5:13 | clean1 | |
110157
| taint.cpp:4:40:4:45 | clean1 | taint.cpp:6:3:6:8 | clean1 | |
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
2+
typedef unsigned long size_t;
3+
4+
namespace std
5+
{
6+
template<class charT> struct char_traits;
7+
8+
typedef size_t streamsize;
9+
10+
template <class T> class allocator {
11+
public:
12+
allocator() throw();
13+
};
14+
15+
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
16+
class basic_string {
17+
public:
18+
explicit basic_string(const Allocator& a = Allocator());
19+
basic_string(const charT* s, const Allocator& a = Allocator());
20+
21+
const charT* c_str() const;
22+
};
23+
24+
typedef basic_string<char> string;
25+
26+
template <class charT, class traits = char_traits<charT> >
27+
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
28+
public:
29+
basic_istream<charT,traits>& operator>>(int& n);
30+
};
31+
32+
template <class charT, class traits = char_traits<charT> >
33+
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
34+
public:
35+
typedef charT char_type;
36+
basic_ostream<charT,traits>& write(const char_type* s, streamsize n);
37+
38+
basic_ostream<charT, traits>& operator<<(int n);
39+
};
40+
41+
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
42+
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
43+
44+
template<class charT, class traits = char_traits<charT>>
45+
class basic_iostream : public basic_istream<charT, traits>, public basic_ostream<charT, traits> {
46+
public:
47+
};
48+
49+
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
50+
class basic_stringstream : public basic_iostream<charT, traits> {
51+
public:
52+
explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/);
53+
54+
basic_string<charT, traits, Allocator> str() const;
55+
};
56+
57+
using stringstream = basic_stringstream<char>;
58+
}
59+
60+
char *source();
61+
void sink(const char *s) {};
62+
void sink(const std::string &s) {};
63+
void sink(const std::stringstream &s) {};
64+
65+
void test_string()
66+
{
67+
char *a = source();
68+
std::string b("123");
69+
std::string c(source());
70+
71+
sink(a); // tainted
72+
sink(b);
73+
sink(c); // tainted
74+
sink(b.c_str());
75+
sink(c.c_str()); // tainted
76+
}
77+
78+
void test_stringstream()
79+
{
80+
std::stringstream ss1, ss2, ss3, ss4, ss5;
81+
std::string t(source());
82+
83+
ss1 << "1234";
84+
ss2 << source();
85+
ss3 << "123" << source();
86+
ss4 << source() << "456";
87+
ss5 << t;
88+
89+
sink(ss1);
90+
sink(ss2); // tainted [NOT DETECTED]
91+
sink(ss3); // tainted [NOT DETECTED]
92+
sink(ss4); // tainted [NOT DETECTED]
93+
sink(ss5); // tainted [NOT DETECTED]
94+
sink(ss1.str());
95+
sink(ss2.str()); // tainted [NOT DETECTED]
96+
sink(ss3.str()); // tainted [NOT DETECTED]
97+
sink(ss4.str()); // tainted [NOT DETECTED]
98+
sink(ss5.str()); // tainted [NOT DETECTED]
99+
}
100+
101+
void test_stringstream_int(int source)
102+
{
103+
std::stringstream ss1, ss2;
104+
105+
ss1 << 1234;
106+
ss2 << source;
107+
108+
sink(ss1);
109+
sink(ss2); // tainted [NOT DETECTED]
110+
sink(ss1.str());
111+
sink(ss2.str()); // tainted [NOT DETECTED]
112+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
| format.cpp:96:8:96:13 | buffer | format.cpp:95:30:95:43 | call to source |
99
| format.cpp:101:8:101:13 | buffer | format.cpp:100:31:100:45 | call to source |
1010
| format.cpp:106:8:106:14 | wbuffer | format.cpp:105:38:105:52 | call to source |
11+
| stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source |
12+
| stl.cpp:73:7:73:7 | c | stl.cpp:69:16:69:21 | call to source |
13+
| stl.cpp:75:9:75:13 | call to c_str | stl.cpp:69:16:69:21 | call to source |
1114
| taint.cpp:8:8:8:13 | clean1 | taint.cpp:4:27:4:33 | source1 |
1215
| taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source |
1316
| taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source |

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
| format.cpp:96:8:96:13 | format.cpp:95:30:95:43 | AST only |
99
| format.cpp:101:8:101:13 | format.cpp:100:31:100:45 | AST only |
1010
| format.cpp:106:8:106:14 | format.cpp:105:38:105:52 | AST only |
11+
| stl.cpp:73:7:73:7 | stl.cpp:69:16:69:21 | AST only |
12+
| stl.cpp:75:9:75:13 | stl.cpp:69:16:69:21 | AST only |
1113
| taint.cpp:41:7:41:13 | taint.cpp:35:12:35:17 | AST only |
1214
| taint.cpp:42:7:42:13 | taint.cpp:35:12:35:17 | AST only |
1315
| taint.cpp:43:7:43:13 | taint.cpp:37:22:37:27 | AST only |

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
| stl.cpp:71:7:71:7 | (const char *)... | stl.cpp:67:12:67:17 | call to source |
2+
| stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source |
13
| taint.cpp:8:8:8:13 | clean1 | taint.cpp:4:27:4:33 | source1 |
24
| taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source |
35
| taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source |

0 commit comments

Comments
 (0)