13
13
*/
14
14
15
15
import cpp
16
- import semmle.code.cpp.dataflow.TaintTracking
16
+
17
+ predicate argumentMayBeRoot ( Expr e ) {
18
+ e .getValue ( ) = "0" or
19
+ e .( VariableAccess ) .getTarget ( ) .getName ( ) .matches ( "%oot%" )
20
+ }
17
21
18
22
class SetuidLikeFunctionCall extends FunctionCall {
19
23
SetuidLikeFunctionCall ( ) {
24
+ ( getTarget ( ) .hasGlobalName ( "setuid" ) or getTarget ( ) .hasGlobalName ( "setresuid" ) ) and
20
25
// setuid/setresuid with the root user are false positives.
21
- getTarget ( ) .hasGlobalName ( "setuid" ) or
22
- getTarget ( ) .hasGlobalName ( "setresuid" )
26
+ not argumentMayBeRoot ( getArgument ( 0 ) )
23
27
}
24
28
}
25
29
@@ -41,13 +45,16 @@ class SetuidLikeWrapperCall extends FunctionCall {
41
45
42
46
class CallBeforeSetuidFunctionCall extends FunctionCall {
43
47
CallBeforeSetuidFunctionCall ( ) {
44
- // setgid/setresgid with the root group are false positives.
45
- getTarget ( ) .hasGlobalName ( "setgid" ) or
46
- getTarget ( ) .hasGlobalName ( "setresgid" ) or
47
- // Compatibility may require skipping initgroups and setgroups return checks.
48
- // A stricter best practice is to check the result and errnor for EPERM.
49
- getTarget ( ) .hasGlobalName ( "initgroups" ) or
50
- getTarget ( ) .hasGlobalName ( "setgroups" )
48
+ (
49
+ getTarget ( ) .hasGlobalName ( "setgid" ) or
50
+ getTarget ( ) .hasGlobalName ( "setresgid" ) or
51
+ // Compatibility may require skipping initgroups and setgroups return checks.
52
+ // A stricter best practice is to check the result and errnor for EPERM.
53
+ getTarget ( ) .hasGlobalName ( "initgroups" ) or
54
+ getTarget ( ) .hasGlobalName ( "setgroups" )
55
+ ) and
56
+ // setgid/setresgid/etc with the root group are false positives.
57
+ not argumentMayBeRoot ( getArgument ( 0 ) )
51
58
}
52
59
}
53
60
@@ -73,12 +80,11 @@ predicate setuidBeforeSetgid(
73
80
setgidWrapper .getAPredecessor + ( ) = setuidWrapper
74
81
}
75
82
76
- predicate flowsToCondition ( Expr fc ) {
77
- exists ( DataFlow:: Node source , DataFlow:: Node sink |
78
- TaintTracking:: localTaint ( source , sink ) and
79
- fc = source .asExpr ( ) and
80
- ( sink .asExpr ( ) .getParent * ( ) .( ControlFlowNode ) .isCondition ( ) or sink .asExpr ( ) .isCondition ( ) )
81
- )
83
+ predicate isAccessed ( FunctionCall fc ) {
84
+ exists ( Variable v | v .getAnAssignedValue ( ) = fc ) or
85
+ exists ( Operation c | fc = c .getAChild ( ) | c .isCondition ( ) ) or
86
+ // ignore pattern where result is intentionally ignored by a cast to void.
87
+ fc .hasExplicitConversion ( )
82
88
}
83
89
84
90
from
87
93
SetuidLikeFunctionCall setuid
88
94
where
89
95
setuidBeforeSetgid ( setuid , fc ) and
90
- // Require the call return code to be used in a condition.
96
+ // Require the call return code to be used in a condition or assigned .
91
97
// This introduces false negatives where the return is checked but then
92
98
// errno == EPERM allows execution to continue.
93
- ( not flowsToCondition ( fc ) or not flowsToCondition ( setuid ) ) and
99
+ not isAccessed ( fc ) and
94
100
func = fc .getEnclosingFunction ( )
95
101
select fc , "This function is called within " + func + ", and potentially after " +
96
- "$@, and may not succeed. Be sure to check the return code and errno." ,
102
+ "$@, and may not succeed. Be sure to check the return code and errno, otherwise permissions " +
103
+ "may not be dropped." ,
97
104
setuid , setuid .getTarget ( ) .getName ( )
0 commit comments