Skip to content

Commit f1ad0da

Browse files
authored
Merge pull request github#2849 from geoffw0/model-gets
C++: Model for gets
2 parents 93c6f8f + 44c66a3 commit f1ad0da

File tree

11 files changed

+85
-7
lines changed

11 files changed

+85
-7
lines changed

change-notes/1.24/analysis-cpp.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
4444
* The `LocalScopeVariableReachability` library is deprecated in favor of
4545
`StackVariableReachability`. The functionality is the same.
4646
* The models library models `strlen` in more detail, and includes common variations such as `wcslen`.
47+
* The models library models `gets` and similar functions.
4748
* The taint tracking library (`semmle.code.cpp.dataflow.TaintTracking`) has had
4849
the following improvements:
4950
* The library now models data flow through `strdup` and similar functions.

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
7676
}
7777

7878
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
79+
80+
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
7981
}
8082

8183
private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
@@ -96,6 +98,8 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
9698
}
9799

98100
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
101+
102+
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
99103
}
100104

101105
private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
@@ -119,6 +123,8 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
119123
}
120124

121125
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
126+
127+
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
122128
}
123129

124130
private predicate readsVariable(LoadInstruction load, Variable var) {
@@ -153,6 +159,11 @@ private predicate nodeIsBarrier(DataFlow::Node node) {
153159
)
154160
}
155161

162+
private predicate nodeIsBarrierIn(DataFlow::Node node) {
163+
// don't use dataflow into taint sources, as this leads to duplicate results.
164+
node = getNodeForSource(any(Expr e))
165+
}
166+
156167
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
157168
// Expressions computed from tainted data are also tainted
158169
exists(CallInstruction call, int argIndex | call = i2 |

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
private import implementations.Allocation
22
private import implementations.Deallocation
33
private import implementations.Fread
4+
private import implementations.Gets
45
private import implementations.IdentityFunction
56
private import implementations.Inet
67
private import implementations.Memcpy
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import semmle.code.cpp.models.interfaces.DataFlow
2+
import semmle.code.cpp.models.interfaces.Taint
3+
import semmle.code.cpp.models.interfaces.ArrayFunction
4+
import semmle.code.cpp.models.interfaces.Alias
5+
import semmle.code.cpp.models.interfaces.SideEffect
6+
7+
/**
8+
* The standard functions `gets` and `fgets`.
9+
*/
10+
class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
11+
SideEffectFunction {
12+
GetsFunction() {
13+
exists(string name | hasGlobalOrStdName(name) |
14+
name = "gets" or // gets(str)
15+
name = "fgets" or // fgets(str, num, stream)
16+
name = "fgetws" // fgetws(wstr, num, stream)
17+
)
18+
}
19+
20+
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
21+
input.isParameter(0) and
22+
output.isReturnValue()
23+
}
24+
25+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
26+
input.isParameter(2) and
27+
output.isParameterDeref(0)
28+
}
29+
30+
override predicate parameterNeverEscapes(int index) { index = 2 }
31+
32+
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
33+
34+
override predicate parameterIsAlwaysReturned(int index) { index = 0 }
35+
36+
override predicate hasOnlySpecificReadSideEffects() { any() }
37+
38+
override predicate hasOnlySpecificWriteSideEffects() { any() }
39+
40+
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
41+
i = 0 and
42+
buffer = true and
43+
mustWrite = true
44+
}
45+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,9 @@
6767
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... | |
6868
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | |
6969
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy | |
70+
| test.cpp:100:12:100:15 | call to gets | test.cpp:98:8:98:14 | pointer | |
71+
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:2:100:8 | pointer | |
72+
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:12:100:15 | call to gets | |
73+
| test.cpp:100:17:100:22 | buffer | test.cpp:93:18:93:18 | s | |
74+
| test.cpp:100:17:100:22 | buffer | test.cpp:97:7:97:12 | buffer | |
75+
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | buffer | |

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@
1111
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
1212
| test.cpp:83:28:83:33 | call to getenv | test.cpp:85:8:85:11 | copy | AST only |
1313
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:9:86:12 | copy | AST only |
14+
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:2:100:8 | pointer | AST only |
15+
| test.cpp:100:17:100:22 | buffer | test.cpp:97:7:97:12 | buffer | AST only |
16+
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | array to pointer conversion | IR only |

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,8 @@
5252
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... | |
5353
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | |
5454
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy | |
55+
| test.cpp:100:12:100:15 | call to gets | test.cpp:98:8:98:14 | pointer | |
56+
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:12:100:15 | call to gets | |
57+
| test.cpp:100:17:100:22 | buffer | test.cpp:93:18:93:18 | s | |
58+
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | array to pointer conversion | |
59+
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | buffer | |

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,14 @@ void mallocBuffer() {
8888
if (!strcmp(copy, "admin")) { // copy should be tainted
8989
isAdmin = true;
9090
}
91-
}
91+
}
92+
93+
char *gets(char *s);
94+
95+
void test_gets()
96+
{
97+
char buffer[1024];
98+
char *pointer;
99+
100+
pointer = gets(buffer);
101+
}
Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
| tests.c:28:3:28:9 | call to sprintf | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
22
| tests.c:29:3:29:9 | call to sprintf | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
3-
| tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
4-
| tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
53
| tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:31:15:31:23 | buffer100 | buffer100 |
6-
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
7-
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
8-
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:31:15:31:23 | buffer100 | buffer100 |
94
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:33:21:33:29 | buffer100 | buffer100 |
105
| tests.c:34:25:34:33 | buffer100 | This 'sscanf string argument' with input from $@ may overflow the destination. | tests.c:34:10:34:13 | argv | argv |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
| funcsLocal.c:17:9:17:10 | i1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:16:8:16:9 | i1 | fread |
22
| funcsLocal.c:27:9:27:10 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:26:8:26:9 | i3 | fgets |
33
| funcsLocal.c:32:9:32:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:31:13:31:17 | call to fgets | fgets |
4+
| funcsLocal.c:32:9:32:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:31:19:31:21 | i41 | fgets |
45
| funcsLocal.c:37:9:37:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:36:7:36:8 | i5 | gets |
56
| funcsLocal.c:42:9:42:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:41:13:41:16 | call to gets | gets |
7+
| funcsLocal.c:42:9:42:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:41:18:41:20 | i61 | gets |

0 commit comments

Comments
 (0)