Skip to content

Commit 7d9ebaf

Browse files
authored
Merge pull request #5040 from MathiasVP/strset-and-strtok-models
C++: Strset and strtok model implementations
2 parents 768be9e + 23eb4d2 commit 7d9ebaf

File tree

5 files changed

+161
-0
lines changed

5 files changed

+161
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ private import implementations.Strcat
1515
private import implementations.Strcpy
1616
private import implementations.Strdup
1717
private import implementations.Strftime
18+
private import implementations.Strtok
19+
private import implementations.Strset
1820
private import implementations.StdContainer
1921
private import implementations.StdPair
2022
private import implementations.StdMap
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Provides implementation classes modeling `strset` and various similar
3+
* functions. See `semmle.code.cpp.models.Models` for usage information.
4+
*/
5+
6+
import semmle.code.cpp.Function
7+
import semmle.code.cpp.models.interfaces.ArrayFunction
8+
import semmle.code.cpp.models.interfaces.DataFlow
9+
import semmle.code.cpp.models.interfaces.Alias
10+
import semmle.code.cpp.models.interfaces.SideEffect
11+
12+
/**
13+
* The standard function `strset` and its assorted variants
14+
*/
15+
private class StrsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
16+
SideEffectFunction {
17+
StrsetFunction() {
18+
hasGlobalName([
19+
"strset", "_strset", "_strset_l", "_wcsset", "_wcsset_l", "_mbsset", "_mbsset_l",
20+
"_mbsnbset", "_mbsnbset_l", "_strnset", "_strnset_l", "_wcsnset", "_wcsnset_l", "_mbsnset",
21+
"_mbsnset_l"
22+
])
23+
}
24+
25+
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
26+
27+
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
28+
29+
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
30+
31+
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
32+
// flow from the character that overrides the string
33+
input.isParameter(1) and
34+
(
35+
output.isReturnValueDeref()
36+
or
37+
output.isParameterDeref(0)
38+
)
39+
or
40+
// flow from the input string to the output string
41+
input.isParameter(0) and
42+
output.isReturnValue()
43+
}
44+
45+
override predicate parameterNeverEscapes(int index) { none() }
46+
47+
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
48+
49+
override predicate parameterIsAlwaysReturned(int index) { index = 0 }
50+
51+
override predicate hasOnlySpecificReadSideEffects() { any() }
52+
53+
override predicate hasOnlySpecificWriteSideEffects() { any() }
54+
55+
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
56+
i = 0 and buffer = true and mustWrite = true
57+
}
58+
59+
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
60+
i = 0 and buffer = true
61+
}
62+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Provides implementation classes modeling `strtok` and various similar
3+
* functions. See `semmle.code.cpp.models.Models` for usage information.
4+
*/
5+
6+
import semmle.code.cpp.Function
7+
import semmle.code.cpp.models.interfaces.ArrayFunction
8+
import semmle.code.cpp.models.interfaces.ArrayFunction
9+
import semmle.code.cpp.models.interfaces.Alias
10+
import semmle.code.cpp.models.interfaces.SideEffect
11+
import semmle.code.cpp.models.interfaces.Taint
12+
13+
/**
14+
* The standard function `strtok` and its assorted variants
15+
*/
16+
private class Strtok extends ArrayFunction, AliasFunction, TaintFunction, SideEffectFunction {
17+
Strtok() {
18+
this.hasGlobalOrStdName("strtok") or
19+
this.hasGlobalName(["strtok_r", "_strtok_l", "wcstok", "_wcstok_l", "_mbstok", "_mbstok_l"])
20+
}
21+
22+
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = [0, 1] }
23+
24+
override predicate hasArrayInput(int bufParam) { bufParam = [0, 1] }
25+
26+
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
27+
28+
override predicate parameterNeverEscapes(int index) { index = 1 }
29+
30+
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
31+
32+
override predicate parameterIsAlwaysReturned(int index) { none() }
33+
34+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
35+
input.isParameter(0) and output.isReturnValue()
36+
}
37+
38+
override predicate hasOnlySpecificReadSideEffects() { none() }
39+
40+
override predicate hasOnlySpecificWriteSideEffects() { none() }
41+
42+
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
43+
i = 0 and buffer = true and mustWrite = false
44+
}
45+
46+
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
47+
i = [0, 1] and buffer = true
48+
}
49+
}

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5861,6 +5861,28 @@
58615861
| taint.cpp:483:18:483:19 | ref arg & ... | taint.cpp:483:19:483:19 | n [inner post update] | |
58625862
| taint.cpp:483:19:483:19 | n | taint.cpp:483:18:483:19 | & ... | |
58635863
| taint.cpp:483:28:483:34 | source1 | taint.cpp:483:11:483:15 | ref arg & ... | TAINT |
5864+
| taint.cpp:492:24:492:29 | source | taint.cpp:494:27:494:32 | source | |
5865+
| taint.cpp:493:22:493:29 | ,.-;:_ | taint.cpp:494:35:494:39 | delim | |
5866+
| taint.cpp:493:22:493:29 | ,.-;:_ | taint.cpp:496:7:496:11 | delim | |
5867+
| taint.cpp:494:20:494:25 | call to strtok | taint.cpp:495:7:495:15 | tokenized | |
5868+
| taint.cpp:494:27:494:32 | source | taint.cpp:494:20:494:25 | call to strtok | TAINT |
5869+
| taint.cpp:503:26:503:28 | ptr | taint.cpp:504:10:504:12 | ptr | |
5870+
| taint.cpp:503:26:503:28 | ptr | taint.cpp:505:7:505:9 | ptr | |
5871+
| taint.cpp:503:26:503:28 | ptr | taint.cpp:506:8:506:10 | ptr | |
5872+
| taint.cpp:503:36:503:41 | source | taint.cpp:504:15:504:20 | source | |
5873+
| taint.cpp:504:10:504:12 | ptr | taint.cpp:504:2:504:8 | call to _strset | |
5874+
| taint.cpp:504:10:504:12 | ref arg ptr | taint.cpp:505:7:505:9 | ptr | |
5875+
| taint.cpp:504:10:504:12 | ref arg ptr | taint.cpp:506:8:506:10 | ptr | |
5876+
| taint.cpp:504:15:504:20 | source | taint.cpp:504:2:504:8 | call to _strset | TAINT |
5877+
| taint.cpp:504:15:504:20 | source | taint.cpp:504:10:504:12 | ref arg ptr | |
5878+
| taint.cpp:505:7:505:9 | ref arg ptr | taint.cpp:506:8:506:10 | ptr | |
5879+
| taint.cpp:506:8:506:10 | ptr | taint.cpp:506:7:506:10 | * ... | TAINT |
5880+
| taint.cpp:509:26:509:31 | source | taint.cpp:510:10:510:15 | source | |
5881+
| taint.cpp:509:26:509:31 | source | taint.cpp:511:7:511:12 | source | |
5882+
| taint.cpp:510:10:510:15 | ref arg source | taint.cpp:511:7:511:12 | source | |
5883+
| taint.cpp:510:10:510:15 | source | taint.cpp:510:2:510:8 | call to _strset | |
5884+
| taint.cpp:510:18:510:18 | 0 | taint.cpp:510:2:510:8 | call to _strset | TAINT |
5885+
| taint.cpp:510:18:510:18 | 0 | taint.cpp:510:10:510:15 | ref arg source | |
58645886
| vector.cpp:16:43:16:49 | source1 | vector.cpp:17:26:17:32 | source1 | |
58655887
| vector.cpp:16:43:16:49 | source1 | vector.cpp:31:38:31:44 | source1 | |
58665888
| vector.cpp:17:21:17:33 | call to vector | vector.cpp:19:14:19:14 | v | |

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,3 +484,29 @@ void test_getdelim(FILE* source1) {
484484

485485
sink(line); // $ ir,ast
486486
}
487+
488+
// --- strtok ---
489+
490+
char *strtok(char *str, const char *delim);
491+
492+
void test_strtok(char *source) {
493+
const char* delim = ",.-;:_";
494+
char* tokenized = strtok(source, delim);
495+
sink(tokenized); // $ ast,ir
496+
sink(delim);
497+
}
498+
499+
// --- strset ---
500+
501+
char *_strset(char *str, int c);
502+
503+
void test_strset_1(char* ptr, char source) {
504+
_strset(ptr, source);
505+
sink(ptr); // $ SPURIOUS: ast,ir
506+
sink(*ptr); // $ ast,ir
507+
}
508+
509+
void test_strset_2(char* source) {
510+
_strset(source, 0);
511+
sink(source); // $ ast,ir
512+
}

0 commit comments

Comments
 (0)