Skip to content

Commit 2e883ab

Browse files
authored
Merge pull request #2760 from geoffw0/defaulttainttest3
C++: Emulate old security library's use of predictable more accurately.
2 parents 75bdf42 + 2dfeafa commit 2e883ab

File tree

4 files changed

+47
-25
lines changed

4 files changed

+47
-25
lines changed

cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,40 @@ private predicate predictableInstruction(Instruction instr) {
2121
predictableInstruction(instr.(UnaryInstruction).getUnary())
2222
}
2323

24+
/**
25+
* Functions that we should only allow taint to flow through (to the return
26+
* value) if all but the source argument are 'predictable'. This is done to
27+
* emulate the old security library's implementation rather than due to any
28+
* strong belief that this is the right approach.
29+
*
30+
* Note that the list itself is not very principled; it consists of all the
31+
* functions listed in the old security library's [default] `isPureFunction`
32+
* that have more than one argument, but are not in the old taint tracking
33+
* library's `returnArgument` predicate. In addition, `strlen` is included
34+
* because it's also a special case in flow to return values.
35+
*/
36+
predicate predictableOnlyFlow(string name) {
37+
name = "strcasestr" or
38+
name = "strchnul" or
39+
name = "strchr" or
40+
name = "strchrnul" or
41+
name = "strcmp" or
42+
name = "strcspn" or
43+
name = "strlen" or // special case
44+
name = "strncmp" or
45+
name = "strndup" or
46+
name = "strnlen" or
47+
name = "strrchr" or
48+
name = "strspn" or
49+
name = "strstr" or
50+
name = "strtod" or
51+
name = "strtof" or
52+
name = "strtol" or
53+
name = "strtoll" or
54+
name = "strtoq" or
55+
name = "strtoul"
56+
}
57+
2458
private DataFlow::Node getNodeForSource(Expr source) {
2559
isUserInput(source, _) and
2660
(
@@ -123,15 +157,16 @@ private predicate nodeIsBarrier(DataFlow::Node node) {
123157

124158
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
125159
// Expressions computed from tainted data are also tainted
126-
i2 =
127-
any(CallInstruction call |
128-
isPureFunction(call.getStaticCallTarget().getName()) and
129-
call.getAnArgument() = i1 and
130-
forall(Instruction arg | arg = call.getAnArgument() | arg = i1 or predictableInstruction(arg)) and
131-
// flow through `strlen` tends to cause dubious results, if the length is
132-
// bounded.
133-
not call.getStaticCallTarget().getName() = "strlen"
134-
)
160+
exists(CallInstruction call, int argIndex | call = i2 |
161+
isPureFunction(call.getStaticCallTarget().getName()) and
162+
i1 = getACallArgumentOrIndirection(call, argIndex) and
163+
forall(Instruction arg | arg = call.getAnArgument() |
164+
arg = getACallArgumentOrIndirection(call, argIndex) or predictableInstruction(arg)
165+
) and
166+
// flow through `strlen` tends to cause dubious results, if the length is
167+
// bounded.
168+
not call.getStaticCallTarget().getName() = "strlen"
169+
)
135170
or
136171
// Flow through pointer dereference
137172
i2.(LoadInstruction).getSourceAddress() = i1
@@ -172,7 +207,8 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
172207
any(CallInstruction call |
173208
exists(int indexIn |
174209
modelTaintToReturnValue(call.getStaticCallTarget(), indexIn) and
175-
i1 = getACallArgumentOrIndirection(call, indexIn)
210+
i1 = getACallArgumentOrIndirection(call, indexIn) and
211+
not predictableOnlyFlow(call.getStaticCallTarget().getName())
176212
)
177213
)
178214
or

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
1-
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:6:40:33 | ! ... | IR only |
2-
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:12 | call to strcmp | IR only |
3-
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:33 | (bool)... | IR only |
41
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:15:50:24 | envStr_ptr | AST only |
52
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:28:50:40 | & ... | AST only |
63
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:29:50:40 | envStrGlobal | AST only |
74
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:2:52:12 | * ... | AST only |
85
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:3:52:12 | envStr_ptr | AST only |
9-
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:10:64:14 | bytes | IR only |
10-
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:23 | call to strlen | IR only |
11-
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | (int)... | IR only |
12-
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | ... + ... | IR only |
136
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
147
| test.cpp:68:28:68:33 | call to getenv | test.cpp:67:7:67:13 | copying | AST only |
158
| test.cpp:68:28:68:33 | call to getenv | test.cpp:69:10:69:13 | copy | AST only |

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@
1414
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:14:38:19 | envStr | |
1515
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:28 | call to getenv | |
1616
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:40 | (const char *)... | |
17-
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:6:40:33 | ! ... | |
18-
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:12 | call to strcmp | |
19-
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:33 | (bool)... | |
2017
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:14:40:19 | envStr | |
2118
| test.cpp:49:23:49:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
2219
| test.cpp:49:23:49:28 | call to getenv | test.cpp:45:13:45:24 | envStrGlobal | |
@@ -32,10 +29,6 @@
3229
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:18:60:25 | userName | |
3330
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:34 | call to getenv | |
3431
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:47 | (const char *)... | |
35-
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:10:64:14 | bytes | |
36-
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:23 | call to strlen | |
37-
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | (int)... | |
38-
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | ... + ... | |
3932
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:25:64:32 | userName | |
4033
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
4134
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:17:68:24 | userName | |

cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ void processRequest()
3535
adminPrivileges = 0; // OK, since it's a 0 and not a 1
3636
}
3737

38-
// BAD, but it requires pointer analysis to catch
38+
// BAD (requires pointer analysis to catch)
3939
const char** userp = &currentUser;
4040
*userp = userName;
4141
if (!strcmp(currentUser, "admin")) {

0 commit comments

Comments
 (0)