Skip to content

Commit 473198a

Browse files
committed
C++: Accept any check followed by a 'sensitive' use such as 'chmod'.
1 parent c6d8abc commit 473198a

File tree

3 files changed

+45
-22
lines changed

3 files changed

+45
-22
lines changed

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

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,27 @@ FunctionCall filenameOperation(Expr path) {
2828
exists(string name | name = result.getTarget().getName() |
2929
name =
3030
[
31-
"remove", "unlink", "rmdir", "rename", "chmod", "chown", "fopen", "open", "freopen",
32-
"_open", "_wopen", "_wfopen", "_wfsopen"
31+
"remove", "unlink", "rmdir", "rename", "fopen", "open", "freopen", "_open", "_wopen",
32+
"_wfopen", "_wfsopen"
3333
] and
3434
result.getArgument(0) = path
3535
or
3636
name = ["fopen_s", "wfopen_s", "rename"] and
3737
result.getArgument(1) = path
3838
)
39+
or
40+
result = sensitiveFilenameOperation(path)
41+
}
42+
43+
/**
44+
* An operation on a filename that is likely to modify the security properties
45+
* of the corresponding file and may return an indication of success.
46+
*/
47+
FunctionCall sensitiveFilenameOperation(Expr path) {
48+
exists(string name | name = result.getTarget().getName() |
49+
name = ["chmod", "chown"] and
50+
result.getArgument(0) = path
51+
)
3952
}
4053

4154
/**
@@ -80,29 +93,36 @@ from Expr check, Expr checkPath, FunctionCall use, Expr usePath
8093
where
8194
// `check` looks like a check on a filename
8295
(
83-
// either:
84-
// an access check
85-
check = accessCheck(checkPath)
86-
or
87-
// a stat
88-
check = stat(checkPath, _)
96+
(
97+
// either:
98+
// an access check
99+
check = accessCheck(checkPath)
100+
or
101+
// a stat
102+
check = stat(checkPath, _)
103+
or
104+
// access to a member variable on the stat buf
105+
// (morally, this should be a use-use pair, but it seems unlikely
106+
// that this variable will get reused in practice)
107+
exists(Expr call, Expr e, Variable v |
108+
call = stat(checkPath, e) and
109+
e.getAChild*().(VariableAccess).getTarget() = v and
110+
check.(VariableAccess).getTarget() = v and
111+
not e.getAChild*() = check // the call that writes to the pointer is not where the pointer is checked.
112+
)
113+
) and
114+
// `op` looks like an operation on a filename
115+
use = filenameOperation(usePath)
89116
or
90-
// access to a member variable on the stat buf
91-
// (morally, this should be a use-use pair, but it seems unlikely
92-
// that this variable will get reused in practice)
93-
exists(Expr call, Expr e, Variable v |
94-
call = stat(checkPath, e) and
95-
e.getAChild*().(VariableAccess).getTarget() = v and
96-
check.(VariableAccess).getTarget() = v and
97-
not e.getAChild*() = check // the call that writes to the pointer is not where the pointer is checked.
98-
)
117+
// another filename operation (null pointers can indicate errors)
118+
check = filenameOperation(checkPath) and
119+
// `op` looks like a sensitive operation on a filename
120+
use = sensitiveFilenameOperation(usePath)
99121
) and
100122
// `checkPath` and `usePath` refer to the same SSA variable
101123
exists(SsaDefinition def, StackVariable v |
102124
def.getAUse(v) = checkPath and def.getAUse(v) = usePath
103125
) and
104-
// `op` looks like an operation on a filename
105-
use = filenameOperation(usePath) and
106126
// the return value of `check` is used (possibly with one step of
107127
// variable indirection) in a guard which controls `use`
108128
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
| test2.cpp:130:7:130:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:130:13:130:16 | path | filename | test2.cpp:128:21:128:27 | buf_ptr | checked |
66
| 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 |
77
| 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 |
8+
| 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 |
89
| 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 |
910
| 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 |
1011
| 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 |
12+
| test2.cpp:348:3:348:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:348:9:348:12 | path | filename | test2.cpp:341:6:341:10 | call to fopen | checked |
13+
| test2.cpp:356:3:356:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:356:9:356:13 | path2 | filename | test2.cpp:354:7:354:12 | call to rename | checked |

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ void test4_1(const char *path)
242242

243243
fclose(f);
244244

245-
chmod(path, 0); // DUBIOUS (bad but perhaps not exploitable)
245+
chmod(path, 0); // BAD
246246
}
247247
}
248248

@@ -345,14 +345,14 @@ void test7_1(const char *path)
345345

346346
fclose(f);
347347

348-
chmod(path, 1234); // BAD [NOT DETECTED]
348+
chmod(path, 1234); // BAD
349349
}
350350
}
351351

352352
void test7_1(const char *path1, const char *path2)
353353
{
354354
if (!rename(path1, path2))
355355
{
356-
chmod(path2, 1234); // BAD [NOT DETECTED]
356+
chmod(path2, 1234); // BAD
357357
}
358358
}

0 commit comments

Comments
 (0)