-
Notifications
You must be signed in to change notification settings - Fork 61
/
Copy pathDependenceOnOrderOfFunctionArgumentsForSideEffects.ql
129 lines (124 loc) · 5.08 KB
/
DependenceOnOrderOfFunctionArgumentsForSideEffects.ql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/**
* @id c/cert/dependence-on-order-of-function-arguments-for-side-effects
* @name EXP30-C: Do not depend on the order of evaluation of function call arguments for side effects
* @description Depending on the order of evaluation for side effects in function call arguments can
* result in unexpected program behavior.
* @kind problem
* @precision high
* @problem.severity warning
* @tags external/cert/id/exp30-c
* correctness
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import codingstandards.cpp.SideEffect
import semmle.code.cpp.dataflow.new.DataFlow
import semmle.code.cpp.dataflow.new.TaintTracking
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
/** Holds if the function's return value is derived from the `AliasParamter` p. */
predicate returnValueDependsOnAliasParameter(AliasParameter p) {
exists(ReturnStmt ret | ret = p.getFunction().getBlock().getAStmt() |
TaintTracking::localTaint(DataFlow::parameterNode(p), DataFlow::exprNode(ret.getExpr()))
or
exists(FieldAccess fa, VariableAccess va | fa.getQualifier() = va and va.getTarget() = p |
TaintTracking::localTaint(DataFlow::exprNode(fa), DataFlow::exprNode(ret.getExpr()))
)
or
exists(FunctionCall call, VariableAccess va | call.getQualifier() = va and va.getTarget() = p |
TaintTracking::localTaint(DataFlow::exprNode(call), DataFlow::exprNode(ret.getExpr()))
)
or
exists(VariableAccess va | va.getTarget() = p | ret.getAChild+() = va)
)
or
exists(FunctionCall call, ReturnStmt ret, int i, AliasParameter q |
ret = p.getFunction().getBlock().getAStmt() and call.getEnclosingFunction() = p.getFunction()
|
DataFlow::localFlow(DataFlow::parameterNode(p), DataFlow::exprNode(call.getArgument(i))) and
q = call.getTarget().getParameter(i) and
returnValueDependsOnAliasParameter(q) and
TaintTracking::localTaint(DataFlow::exprNode(call), DataFlow::exprNode(ret.getExpr()))
)
}
/** Holds if the function `f`'s return value is derived from the global variable `v`. */
predicate returnValueDependsOnGlobalVariable(Function f, GlobalVariable v) {
exists(ReturnStmt ret, VariableAccess va |
ret = f.getBlock().getAStmt() and va.getTarget() = v and va.getEnclosingFunction() = f
|
TaintTracking::localTaint(DataFlow::exprNode(va), DataFlow::exprNode(ret.getExpr()))
)
or
exists(ReturnStmt ret, FunctionCall call |
ret = f.getBlock().getAStmt() and
call.getEnclosingFunction() = f and
returnValueDependsOnGlobalVariable(call.getTarget(), v) and
TaintTracking::localTaint(DataFlow::exprNode(call), DataFlow::exprNode(ret.getExpr()))
)
}
/** Holds if the member function `f`'s return value is derived from the member variable `v`. */
predicate returnValueDependsOnMemberVariable(MemberFunction f, MemberVariable v) {
exists(ReturnStmt ret, VariableAccess va |
ret = f.getBlock().getAStmt() and
va.getTarget() = v and
va.getEnclosingFunction() = f and
v.getDeclaringType() = f.getDeclaringType()
|
TaintTracking::localTaint(DataFlow::exprNode(va), DataFlow::exprNode(ret.getExpr()))
)
}
from
FunctionCall call, Function f1, Function f2, int i, int j, FunctionCall arg1, FunctionCall arg2,
Variable v1, Variable v2
where
not isExcluded(call,
SideEffects1Package::dependenceOnOrderOfFunctionArgumentsForSideEffectsQuery()) and
arg1 = call.getArgument(i) and
arg2 = call.getArgument(j) and
i < j and
arg1.getTarget() = f1 and
arg2.getTarget() = f2 and
(
// Considering the shared states:
// - pointer or reference arguments being used in both functions
exists(AliasParameter p1, AliasParameter p2 |
v1 = p1 and
v2 = p2 and
f1.getAParameter() = p1 and
f2.getAParameter() = p2 and
p1.isModified() and
p2.isModified() and
globalValueNumber(arg1.getArgument(p1.getIndex())) =
globalValueNumber(arg2.getArgument(p2.getIndex())) and
returnValueDependsOnAliasParameter(p1) and
returnValueDependsOnAliasParameter(p2)
)
or
// - global variables being used in both functions
exists(GlobalVariable v, VariableEffect ve1, VariableEffect ve2 |
v1 = v and
v2 = v and
returnValueDependsOnGlobalVariable(f1, v) and
returnValueDependsOnGlobalVariable(f2, v) and
ve1.getTarget() = v and
ve2.getTarget() = v
)
or
// - member variables that can be modified in both functions
exists(MemberVariable v |
v1 = v and
v2 = v and
returnValueDependsOnMemberVariable(f1, v) and
returnValueDependsOnMemberVariable(f2, v) and
v = getAMemberVariableEffect(f1).getTarget() and
v = getAMemberVariableEffect(f2).getTarget() and
(
globalValueNumber(arg1.getQualifier()) = globalValueNumber(arg2.getQualifier())
or
v.isStatic() and arg1.getQualifier().getType() = arg2.getQualifier().getType()
)
)
)
select call,
"Depending on the order of evaluation for the arguments $@ and $@ for side effects on shared state is unspecified and can result in unexpected behavior.",
arg1, arg1.toString(), arg2, arg2.toString()