-
Notifications
You must be signed in to change notification settings - Fork 75
Expand file tree
/
Copy pathDoNotPassAliasedPointerToRestrictQualifiedParam.ql
More file actions
186 lines (168 loc) · 6.91 KB
/
DoNotPassAliasedPointerToRestrictQualifiedParam.ql
File metadata and controls
186 lines (168 loc) · 6.91 KB
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/**
* @id c/cert/do-not-pass-aliased-pointer-to-restrict-qualified-param
* @name EXP43-C: Do not pass aliased pointers to restrict-qualified parameters
* @description Passing an aliased pointer to a restrict-qualified parameter is undefined behavior.
* @kind problem
* @precision medium
* @problem.severity error
* @tags external/cert/id/exp43-c
* correctness
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import codingstandards.c.Pointers
import codingstandards.c.Variable
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.pointsto.PointsTo
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
/**
* A function that has a parameter with a restrict-qualified pointer type.
*/
class FunctionWithRestrictParameters extends Function {
Parameter restrictPtrParam;
FunctionWithRestrictParameters() {
restrictPtrParam.getUnspecifiedType() instanceof PointerOrArrayType and
(
restrictPtrParam.getType().hasSpecifier(["restrict"]) and
restrictPtrParam = this.getAParameter()
or
this.hasGlobalName(["strcpy", "strncpy", "strcat", "strncat", "memcpy"]) and
restrictPtrParam = this.getParameter([0, 1])
or
this.hasGlobalName(["strcpy_s", "strncpy_s", "strcat_s", "strncat_s", "memcpy_s"]) and
restrictPtrParam = this.getParameter([0, 2])
or
this.hasGlobalName(["strtok_s"]) and
restrictPtrParam = this.getAParameter()
or
this.hasGlobalName(["printf", "printf_s", "scanf", "scanf_s"]) and
restrictPtrParam = this.getParameter(0)
or
this.hasGlobalName(["sprintf", "sprintf_s", "snprintf", "snprintf_s"]) and
restrictPtrParam = this.getParameter(3)
)
}
Parameter getARestrictPtrParam() { result = restrictPtrParam }
}
/**
* A call to a function that has a parameter with a restrict-qualified pointer type.
*/
class CallToFunctionWithRestrictParameters extends FunctionCall {
CallToFunctionWithRestrictParameters() {
this.getTarget() instanceof FunctionWithRestrictParameters
}
Expr getARestrictPtrArg() {
result =
this.getArgument(this.getTarget()
.(FunctionWithRestrictParameters)
.getARestrictPtrParam()
.getIndex())
}
Expr getAPtrArg(int index) {
result = this.getArgument(index) and
pointerValue(result)
}
Expr getAPossibleSizeArg() {
exists(Parameter param |
param = this.getTarget().(FunctionWithRestrictParameters).getAParameter() and
param.getUnderlyingType() instanceof IntegralType and
// exclude __builtin_object_size
not result.(FunctionCall).getTarget() instanceof BuiltInFunction and
result = this.getArgument(param.getIndex())
)
}
}
/**
* A `PointsToExpr` that is an argument of a pointer-type in a `CallToFunctionWithRestrictParameters`
*/
class CallToFunctionWithRestrictParametersArgExpr extends Expr {
int paramIndex;
CallToFunctionWithRestrictParametersArgExpr() {
this = any(CallToFunctionWithRestrictParameters call).getAPtrArg(paramIndex)
}
int getParamIndex() { result = paramIndex }
}
int getStatedValue(Expr e) {
// `upperBound(e)` defaults to `exprMaxVal(e)` when `e` isn't analyzable. So to get a meaningful
// result in this case we pick the minimum value obtainable from dataflow and range analysis.
result =
upperBound(e)
.minimum(min(Expr source | DataFlow::localExprFlow(source, e) | source.getValue().toInt()))
}
int getPointerArithmeticOperandStatedValue(CallToFunctionWithRestrictParametersArgExpr expr) {
result = getStatedValue(expr.(PointerArithmeticExpr).getOperand())
or
// edge-case: &(array[index]) expressions
result = getStatedValue(expr.(AddressOfExpr).getOperand().(PointerArithmeticExpr).getOperand())
or
// fall-back if `expr` is not a pointer arithmetic expression
not expr instanceof PointerArithmeticExpr and
not expr.(AddressOfExpr).getOperand() instanceof PointerArithmeticExpr and
result = 0
}
class PointerValueToRestrictArgConfig extends DataFlow::Configuration {
PointerValueToRestrictArgConfig() { this = "PointerValueToRestrictArgConfig" }
override predicate isSource(DataFlow::Node source) { pointerValue(source.asExpr()) }
override predicate isSink(DataFlow::Node sink) {
exists(CallToFunctionWithRestrictParameters call |
sink.asExpr() = call.getAPtrArg(_).getAChild*()
)
}
override predicate isBarrierIn(DataFlow::Node node) {
exists(AddressOfExpr a | node.asExpr() = a.getOperand().getAChild*())
}
}
from
PointerValueToRestrictArgConfig config, CallToFunctionWithRestrictParameters call,
CallToFunctionWithRestrictParametersArgExpr arg1,
CallToFunctionWithRestrictParametersArgExpr arg2, int argOffset1, int argOffset2, Expr source1,
Expr source2, string sourceMessage1, string sourceMessage2
where
not isExcluded(call, Pointers3Package::doNotPassAliasedPointerToRestrictQualifiedParamQuery()) and
arg1 = call.getARestrictPtrArg() and
arg2 = call.getAPtrArg(_) and
// enforce ordering to remove permutations if multiple restrict-qualified args exist
(not arg2 = call.getARestrictPtrArg() or arg2.getParamIndex() > arg1.getParamIndex()) and
(
// check if two pointers address the same object
config.hasFlow(DataFlow::exprNode(source1), DataFlow::exprNode(arg1.getAChild*())) and
(
// one pointer value flows to both args
config.hasFlow(DataFlow::exprNode(source1), DataFlow::exprNode(arg2.getAChild*())) and
sourceMessage1 = "$@" and
sourceMessage2 = "source" and
source1 = source2
or
// there are two separate values that flow from an AddressOfExpr of the same target
getAddressOfExprTargetBase(source1) = getAddressOfExprTargetBase(source2) and
config.hasFlow(DataFlow::exprNode(source2), DataFlow::exprNode(arg2.getAChild*())) and
sourceMessage1 = "a pair of address-of expressions ($@, $@)" and
sourceMessage2 = "addressof1" and
not source1 = source2
)
) and
// get the offset of the pointer arithmetic operand (or '0' if there is none)
argOffset1 = getPointerArithmeticOperandStatedValue(arg1) and
argOffset2 = getPointerArithmeticOperandStatedValue(arg2) and
(
// case 1: the pointer args are the same.
// (definite aliasing)
argOffset1 = argOffset2
or
// case 2: the pointer args are different, a size arg exists,
// and the size arg is greater than the difference between the offsets.
// (potential aliasing)
exists(Expr sizeArg |
sizeArg = call.getAPossibleSizeArg() and
getStatedValue(sizeArg) > (argOffset1 - argOffset2).abs()
)
or
// case 3: the pointer args are different, and a size arg does not exist
// (potential aliasing)
not exists(call.getAPossibleSizeArg())
)
select call,
"Call to '" + call.getTarget().getName() + "' passes an $@ to a $@ (pointer value derived from " +
sourceMessage1 + ".", arg2, "aliased pointer", arg1, "restrict-qualified parameter", source1,
sourceMessage2, source2, "addressof2"