Skip to content

Commit 7bc18ab

Browse files
authored
Merge pull request github#6150 from geoffw0/toctou
C++: Tests for cpp/toctou-race-condition
2 parents db76b12 + 0c02989 commit 7bc18ab

File tree

4 files changed

+375
-23
lines changed

4 files changed

+375
-23
lines changed

cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ import cpp
1616
import semmle.code.cpp.controlflow.Guards
1717

1818
/**
19-
* An operation on a filename.
19+
* An operation on a filename that is likely to modify the corresponding file
20+
* and may return an indication of success.
2021
*
21-
* Note: we're not interested in operations on file descriptors, as they
22-
* are better behaved.
22+
* Note: we're not interested in operations where the file is specified by a
23+
* descriptor, rather than a filename, as they are better behaved. We are
24+
* interested in functions that take a filename and return a file descriptor,
25+
* however.
2326
*/
2427
FunctionCall filenameOperation(Expr path) {
2528
exists(string name | name = result.getTarget().getName() |
@@ -48,7 +51,8 @@ FunctionCall filenameOperation(Expr path) {
4851
}
4952

5053
/**
51-
* A use of `access` (or similar) on a filename.
54+
* An operation on a filename that returns information in the return value but
55+
* does not modify the corresponding file. For example, `access`.
5256
*/
5357
FunctionCall accessCheck(Expr path) {
5458
exists(string name | name = result.getTarget().getName() |
@@ -62,7 +66,9 @@ FunctionCall accessCheck(Expr path) {
6266
}
6367

6468
/**
65-
* A use of `stat` (or similar) on a filename.
69+
* An operation on a filename that returns information via a pointer argument
70+
* and any return value, but does not modify the corresponding file. For
71+
* example, `stat`.
6672
*/
6773
FunctionCall stat(Expr path, Expr buf) {
6874
exists(string name | name = result.getTarget().getName() |
@@ -77,7 +83,7 @@ FunctionCall stat(Expr path, Expr buf) {
7783
}
7884

7985
/**
80-
* Holds if `use` points to `source`, either by being the same or by
86+
* Holds if `use` refers to `source`, either by being the same or by
8187
* one step of variable indirection.
8288
*/
8389
predicate referenceTo(Expr source, Expr use) {
@@ -88,36 +94,38 @@ predicate referenceTo(Expr source, Expr use) {
8894
)
8995
}
9096

91-
from FunctionCall fc, Expr check, Expr checkUse, Expr opUse
97+
from Expr check, Expr checkPath, FunctionCall use, Expr usePath
9298
where
93-
// checkUse looks like a check on a filename
99+
// `check` looks like a check on a filename
94100
(
95101
// either:
96102
// an access check
97-
check = accessCheck(checkUse)
103+
check = accessCheck(checkPath)
98104
or
99105
// a stat
100-
check = stat(checkUse, _)
106+
check = stat(checkPath, _)
101107
or
102108
// another filename operation (null pointers can indicate errors)
103-
check = filenameOperation(checkUse)
109+
check = filenameOperation(checkPath)
104110
or
105111
// access to a member variable on the stat buf
106112
// (morally, this should be a use-use pair, but it seems unlikely
107113
// that this variable will get reused in practice)
108-
exists(Variable buf | exists(stat(checkUse, buf.getAnAccess())) |
114+
exists(Variable buf | exists(stat(checkPath, buf.getAnAccess())) |
109115
check.(VariableAccess).getQualifier() = buf.getAnAccess()
110116
)
111117
) and
112-
// checkUse and opUse refer to the same SSA variable
113-
exists(SsaDefinition def, StackVariable v | def.getAUse(v) = checkUse and def.getAUse(v) = opUse) and
114-
// opUse looks like an operation on a filename
115-
fc = filenameOperation(opUse) and
116-
// the return value of check is used (possibly with one step of
117-
// variable indirection) in a guard which controls fc
118+
// `checkPath` and `usePath` refer to the same SSA variable
119+
exists(SsaDefinition def, StackVariable v |
120+
def.getAUse(v) = checkPath and def.getAUse(v) = usePath
121+
) and
122+
// `op` looks like an operation on a filename
123+
use = filenameOperation(usePath) and
124+
// the return value of `check` is used (possibly with one step of
125+
// variable indirection) in a guard which controls `use`
118126
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
119-
guard.controls(fc.(ControlFlowNode).getBasicBlock(), _)
127+
guard.controls(use.(ControlFlowNode).getBasicBlock(), _)
120128
)
121-
select fc,
129+
select use,
122130
"The $@ being operated upon was previously $@, but the underlying file may have been changed since then.",
123-
opUse, "filename", check, "checked"
131+
usePath, "filename", check, "checked"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
| test2.cpp:39:7:39:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:39:13:39:16 | path | filename | test2.cpp:34:6:34:10 | call to fopen | checked |
2+
| test2.cpp:52:7:52:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:52:13:52:16 | path | filename | test2.cpp:52:7:52:11 | call to fopen | checked |
3+
| test2.cpp:69:7:69:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:69:13:69:16 | path | filename | test2.cpp:67:6:67:9 | call to stat | checked |
4+
| test2.cpp:98:7:98:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:98:13:98:16 | path | filename | test2.cpp:96:15:96:17 | foo | checked |
5+
| test2.cpp:157:7:157:10 | call to open | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:157:12:157:15 | path | filename | test2.cpp:155:6:155:9 | call to stat | checked |
6+
| test2.cpp:170:7:170:10 | call to open | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:170:12:170:15 | path | filename | test2.cpp:168:6:168:10 | call to lstat | checked |
7+
| test2.cpp:245:3:245:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:245:9:245:12 | path | filename | test2.cpp:238:6:238:10 | call to fopen | checked |
8+
| test2.cpp:255:3:255:8 | call to remove | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:255:10:255:14 | path1 | filename | test2.cpp:253:6:253:11 | call to rename | checked |
9+
| test2.cpp:277:7:277:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:277:13:277:16 | path | filename | test2.cpp:275:6:275:11 | call to access | checked |
10+
| test2.cpp:303:7:303:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:303:13:303:16 | path | filename | test2.cpp:301:7:301:12 | call to access | checked |
11+
| test2.cpp:317:7:317:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:317:13:317:16 | path | filename | test2.cpp:313:6:313:11 | call to access | checked |
112
| test.cpp:21:3:21:8 | call to remove | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test.cpp:21:10:21:14 | file1 | filename | test.cpp:19:7:19:12 | call to rename | checked |
213
| test.cpp:35:3:35:8 | call to remove | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test.cpp:35:10:35:14 | file1 | filename | test.cpp:32:7:32:12 | call to rename | checked |
314
| test.cpp:49:3:49:8 | call to remove | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test.cpp:49:10:49:14 | file1 | filename | test.cpp:47:7:47:12 | call to rename | checked |

cpp/ql/test/query-tests/Security/CWE/CWE-367/semmle/test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ void test1()
1818
create(file1);
1919
if (!rename(file1, file2))
2020
{
21-
remove(file1); // BAD
21+
remove(file1); // DUBIOUS (bad but perhaps not exploitable) [REPORTED]
2222
}
2323
}
2424

@@ -46,6 +46,6 @@ void test3()
4646
create(file1);
4747
if (!rename(file1, file2))
4848
{
49-
remove(file1); // BAD
49+
remove(file1); // DUBIOUS (bad but perhaps not exploitable) [REPORTED]
5050
}
5151
}

0 commit comments

Comments
 (0)