66 * @kind problem
77 * @problem.severity warning
88 * @security-severity 7.7
9- * @precision medium
9+ * @precision high
1010 * @id cpp/toctou-race-condition
1111 * @tags security
1212 * external/cwe/cwe-367
@@ -26,28 +26,29 @@ import semmle.code.cpp.controlflow.Guards
2626 */
2727FunctionCall filenameOperation ( Expr path ) {
2828 exists ( string name | name = result .getTarget ( ) .getName ( ) |
29- (
30- name = "remove" or
31- name = "unlink" or
32- name = "rmdir" or
33- name = "rename" or
34- name = "chmod" or
35- name = "chown" or
36- name = "fopen" or
37- name = "open" or
38- name = "freopen" or
39- name = "_open" or
40- name = "_wopen" or
41- name = "_wfopen"
42- ) and
29+ name =
30+ [
31+ "remove" , "unlink" , "rmdir" , "rename" , "fopen" , "open" , "freopen" , "_open" , "_wopen" ,
32+ "_wfopen" , "_fsopen" , "_wfsopen"
33+ ] and
4334 result .getArgument ( 0 ) = path
4435 or
45- (
46- name = "fopen_s" or
47- name = "wfopen_s"
48- ) and
36+ name = [ "fopen_s" , "wfopen_s" , "rename" ] and
4937 result .getArgument ( 1 ) = path
5038 )
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+ )
5152}
5253
5354/**
@@ -56,11 +57,7 @@ FunctionCall filenameOperation(Expr path) {
5657 */
5758FunctionCall accessCheck ( Expr path ) {
5859 exists ( string name | name = result .getTarget ( ) .getName ( ) |
59- name = "access" or
60- name = "_access" or
61- name = "_waccess" or
62- name = "_access_s" or
63- name = "_waccess_s"
60+ name = [ "access" , "_access" , "_waccess" , "_access_s" , "_waccess_s" ]
6461 ) and
6562 path = result .getArgument ( 0 )
6663}
@@ -72,9 +69,7 @@ FunctionCall accessCheck(Expr path) {
7269 */
7370FunctionCall stat ( Expr path , Expr buf ) {
7471 exists ( string name | name = result .getTarget ( ) .getName ( ) |
75- name = "stat" or
76- name = "lstat" or
77- name = "fstat" or
72+ name = [ "stat" , "lstat" , "fstat" ] or
7873 name .matches ( "\\_stat%" ) or
7974 name .matches ( "\\_wstat%" )
8075 ) and
@@ -98,29 +93,36 @@ from Expr check, Expr checkPath, FunctionCall use, Expr usePath
9893where
9994 // `check` looks like a check on a filename
10095 (
101- // either:
102- // an access check
103- check = accessCheck ( checkPath )
104- or
105- // a stat
106- 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 )
107116 or
108117 // another filename operation (null pointers can indicate errors)
109- check = filenameOperation ( checkPath )
110- or
111- // access to a member variable on the stat buf
112- // (morally, this should be a use-use pair, but it seems unlikely
113- // that this variable will get reused in practice)
114- exists ( Variable buf | exists ( stat ( checkPath , buf .getAnAccess ( ) ) ) |
115- check .( VariableAccess ) .getQualifier ( ) = buf .getAnAccess ( )
116- )
118+ check = filenameOperation ( checkPath ) and
119+ // `op` looks like a sensitive operation on a filename
120+ use = sensitiveFilenameOperation ( usePath )
117121 ) and
118122 // `checkPath` and `usePath` refer to the same SSA variable
119123 exists ( SsaDefinition def , StackVariable v |
120124 def .getAUse ( v ) = checkPath and def .getAUse ( v ) = usePath
121125 ) and
122- // `op` looks like an operation on a filename
123- use = filenameOperation ( usePath ) and
124126 // the return value of `check` is used (possibly with one step of
125127 // variable indirection) in a guard which controls `use`
126128 exists ( GuardCondition guard | referenceTo ( check , guard .getAChild * ( ) ) |
0 commit comments