-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathUnboundedWrite.ql
More file actions
161 lines (139 loc) · 5.89 KB
/
UnboundedWrite.ql
File metadata and controls
161 lines (139 loc) · 5.89 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
/**
* @name Unbounded write
* @description Buffer write operations that do not control the length
* of data written may overflow.
* @kind path-problem
* @problem.severity error
* @security-severity 9.3
* @precision medium
* @id cpp/unbounded-write
* @tags reliability
* security
* external/cwe/cwe-120
* external/cwe/cwe-787
* external/cwe/cwe-805
*/
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.security.FlowSources as FS
import semmle.code.cpp.dataflow.new.TaintTracking
import semmle.code.cpp.controlflow.IRGuards
import Flow::PathGraph
/*
* --- Summary of CWE-120 alerts ---
*
* The essence of CWE-120 is that string / buffer copies that are
* potentially unbounded, e.g. null terminated string copy,
* should be controlled e.g. by using strncpy instead of strcpy.
* In practice this is divided into several queries that
* handle slightly different sub-cases, exclude some acceptable uses,
* and produce reasonable messages to fit each issue.
*
* cases:
* hasExplicitLimit() exists(getMaxData()) exists(getBufferSize(bw.getDest(), _))) handled by
* NO NO either UnboundedWrite.ql isUnboundedWrite()
* NO YES NO UnboundedWrite.ql isMaybeUnboundedWrite()
* NO YES YES VeryLikelyOverrunWrite.ql, OverrunWrite.ql, OverrunWriteFloat.ql
* YES either YES BadlyBoundedWrite.ql
* YES either NO (assumed OK)
*/
/*
* --- CWE-120/UnboundedWrite ---
*/
predicate isUnboundedWrite(BufferWrite bw) {
not bw.hasExplicitLimit() and // has no explicit size limit
not exists(bw.getMaxData(_)) // and we can't deduce an upper bound to the amount copied
}
/**
* Holds if `e` is a source buffer going into an unbounded write `bw` or a
* qualifier of (a qualifier of ...) such a source.
*/
predicate unboundedWriteSource(Expr e, BufferWrite bw, boolean qualifier) {
isUnboundedWrite(bw) and e = bw.getASource() and qualifier = false
or
exists(FieldAccess fa | unboundedWriteSource(fa, bw, _) and e = fa.getQualifier()) and
qualifier = true
}
predicate isSource(FS::FlowSource source, string sourceType) { source.getSourceType() = sourceType }
predicate isSink(DataFlow::Node sink, BufferWrite bw, boolean qualifier) {
unboundedWriteSource(sink.asIndirectExpr(), bw, qualifier)
or
// `gets` and `scanf` reads from stdin so there's no real input.
// The `BufferWrite` library models this as the call itself being
// the source. In this case we mark the output argument as being
// the sink so that we report a path where source = sink (because
// the same output argument is also included in `isSource`).
bw.getASource() = bw and
unboundedWriteSource(sink.asDefiningArgument(), bw, qualifier)
}
/**
* A configuration that specifies flow from a `FlowSource` to a relational
* comparison.
*
* This configuration is used to speed up the barrier computations in Config.
*/
module BarrierConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { isSource(source, _) }
predicate isSink(DataFlow::Node sink) {
comparesEq(sink.asOperand(), _, _, true, _) or
comparesLt(sink.asOperand(), _, _, true, _)
}
}
module BarrierFlow = TaintTracking::Global<BarrierConfig>;
import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
/**
* Holds if `left` is a left operand of some relational comparison that may
* depend on user input.
*/
predicate interestingLessThanOrEqual(Operand left) {
exists(DataFlowImplCommon::NodeEx node |
node.asNode().asOperand() = left and
BarrierFlow::Stages::Stage1::sinkNode(node, _)
)
}
predicate lessThanOrEqual(IRGuardCondition g, Expr e, boolean branch) {
exists(Operand left |
g.comparesLt(left, _, _, true, branch) or
g.comparesEq(left, _, _, true, branch)
|
interestingLessThanOrEqual(left) and
left.getDef().getConvertedResultExpression() = e
)
}
module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { isSource(source, _) }
predicate isSink(DataFlow::Node sink) { isSink(sink, _, _) }
predicate isBarrierOut(DataFlow::Node node) { isSink(node, _, false) }
predicate isBarrier(DataFlow::Node node) {
// Block flow if the node is guarded by any <, <= or = operations.
node = DataFlow::BarrierGuard<lessThanOrEqual/3>::getABarrierNode() or
node = DataFlow::BarrierGuard<lessThanOrEqual/3>::getAnIndirectBarrierNode()
}
predicate observeDiffInformedIncrementalMode() { any() }
Location getASelectedSinkLocation(DataFlow::Node sink) {
exists(BufferWrite bw | result = [bw.getLocation(), sink.getLocation()] | isSink(sink, bw, _))
}
}
module Flow = TaintTracking::Global<Config>;
/*
* An unbounded write is, for example `strcpy(..., tainted)`. We're looking
* for a tainted source buffer of an unbounded write, where this source buffer
* is a sink in the taint-tracking analysis.
*
* In the case of `gets` and `scanf`, where the source buffer is implicit, the
* `BufferWrite` library reports the source buffer to be the same as the
* destination buffer. So to report an alert on a pattern like:
* ```
* char s[32];
* gets(s);
* ```
* we define the sink as the node corresponding to the output argument of `gets`.
* This gives us a path where the source is equal to the sink.
*/
from BufferWrite bw, Flow::PathNode source, Flow::PathNode sink, string sourceType
where
Flow::flowPath(source, sink) and
isSource(source.getNode(), sourceType) and
isSink(sink.getNode(), bw, _)
select bw, source, sink,
"This '" + bw.getBWDesc() + "' with input from $@ may overflow the destination.",
source.getNode(), sourceType