Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit aa6bf67

Browse files
committed
Merge branch 'main' into add-more-invalid-deref-documentation
2 parents f0ab3a3 + 1fa6511 commit aa6bf67

73 files changed

Lines changed: 1117 additions & 841 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/AllocationToInvalidPointer.qll

Lines changed: 45 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -92,59 +92,57 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
9292
* ```
9393
* In this case, the sink pair identified by the product flow library (without any additional barriers)
9494
* would be `(p, n)` (where `n` is the `n` in `p[n]`), because there exists a pointer-arithmetic
95-
* instruction `pai` such that:
96-
* 1. The left-hand of `pai` flows from the allocation, and
97-
* 2. The right-hand of `pai` is non-strictly upper bounded by `n` (where `n` is the `n` in `p[n]`)
95+
* instruction `pai = a + b` such that:
96+
* 1. the allocation flows to `a`, and
97+
* 2. `b <= n` where `n` is the `n` in `p[n]`
9898
* but because there's a strict comparison that compares `n` against the size of the allocation this
9999
* snippet is fine.
100100
*/
101-
module Barrier2 {
102-
private class FlowState2 = int;
103-
104-
private module BarrierConfig2 implements DataFlow::ConfigSig {
101+
module SizeBarrier {
102+
private module SizeBarrierConfig implements DataFlow::ConfigSig {
105103
predicate isSource(DataFlow::Node source) {
106104
// The sources is the same as in the sources for the second
107105
// projection in the `AllocToInvalidPointerConfig` module.
108106
hasSize(_, source, _)
109107
}
110108

111109
additional predicate isSink(
112-
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, FlowState2 state,
113-
boolean testIsTrue
110+
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
114111
) {
115-
// The sink is any "large" side of a relational comparison.
116-
g.comparesLt(left.asOperand(), right.asOperand(), state, true, testIsTrue)
112+
// The sink is any "large" side of a relational comparison. i.e., the `right` expression
113+
// in a guard such as `left < right + k`.
114+
g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue)
117115
}
118116

119117
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
120118
}
121119

122-
private import DataFlow::Global<BarrierConfig2>
120+
private import DataFlow::Global<SizeBarrierConfig>
123121

124-
private FlowState2 getAFlowStateForNode(DataFlow::Node node) {
122+
private int getAFlowStateForNode(DataFlow::Node node) {
125123
exists(DataFlow::Node source |
126124
flow(source, node) and
127125
hasSize(_, source, result)
128126
)
129127
}
130128

131129
private predicate operandGuardChecks(
132-
IRGuardCondition g, Operand left, Operand right, FlowState2 state, boolean edge
130+
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
133131
) {
134-
exists(DataFlow::Node nLeft, DataFlow::Node nRight, FlowState2 state0 |
132+
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
135133
nRight.asOperand() = right and
136134
nLeft.asOperand() = left and
137-
BarrierConfig2::isSink(nLeft, nRight, g, state0, edge) and
135+
SizeBarrierConfig::isSink(nLeft, nRight, g, k, edge) and
138136
state = getAFlowStateForNode(nRight) and
139-
state0 <= state
137+
k <= state
140138
)
141139
}
142140

143141
/**
144142
* Gets an instruction that is guarded by a guard condition which ensures that
145143
* the value of the instruction is upper-bounded by size of some allocation.
146144
*/
147-
Instruction getABarrierInstruction(FlowState2 state) {
145+
Instruction getABarrierInstruction(int state) {
148146
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge |
149147
use = value.getAUse() and
150148
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _,
@@ -158,17 +156,15 @@ module Barrier2 {
158156
* Gets a `DataFlow::Node` that is guarded by a guard condition which ensures that
159157
* the value of the node is upper-bounded by size of some allocation.
160158
*/
161-
DataFlow::Node getABarrierNode(FlowState2 state) {
159+
DataFlow::Node getABarrierNode(int state) {
162160
result.asOperand() = getABarrierInstruction(state).getAUse()
163161
}
164162

165163
/**
166164
* Gets the block of a node that is guarded (see `getABarrierInstruction` or
167165
* `getABarrierNode` for the definition of what it means to be guarded).
168166
*/
169-
IRBlock getABarrierBlock(FlowState2 state) {
170-
result.getAnInstruction() = getABarrierInstruction(state)
171-
}
167+
IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) }
172168
}
173169

174170
private module InterestingPointerAddInstruction {
@@ -201,38 +197,38 @@ private module InterestingPointerAddInstruction {
201197
}
202198

203199
/**
204-
* A product-flow configuration for flow from an `(allocation, size)` pair to a pointer-
205-
* arithmetic instruction that is non-strictly upper-bounded by `allocation + size`.
200+
* A product-flow configuration for flow from an `(allocation, size)` pair to a
201+
* pointer-arithmetic operation `pai` such that `pai <= allocation + size`.
206202
*/
207203
private module Config implements ProductFlow::StateConfigSig {
208204
class FlowState1 = Unit;
209205

210206
class FlowState2 = int;
211207

212208
predicate isSourcePair(
213-
DataFlow::Node source1, FlowState1 state1, DataFlow::Node source2, FlowState2 state2
209+
DataFlow::Node allocSource, FlowState1 unit, DataFlow::Node sizeSource, FlowState2 sizeAddend
214210
) {
215211
// In the case of an allocation like
216212
// ```cpp
217213
// malloc(size + 1);
218214
// ```
219215
// we use `state2` to remember that there was an offset (in this case an offset of `1`) added
220216
// to the size of the allocation. This state is then checked in `isSinkPair`.
221-
exists(state1) and
222-
hasSize(source1.asConvertedExpr(), source2, state2)
217+
exists(unit) and
218+
hasSize(allocSource.asConvertedExpr(), sizeSource, sizeAddend)
223219
}
224220

225221
predicate isSinkPair(
226-
DataFlow::Node sink1, FlowState1 state1, DataFlow::Node sink2, FlowState2 state2
222+
DataFlow::Node allocSink, FlowState1 unit, DataFlow::Node sizeSink, FlowState2 sizeAddend
227223
) {
228-
exists(state1) and
224+
exists(unit) and
229225
// We check that the delta computed by the range analysis matches the
230226
// state value that we set in `isSourcePair`.
231-
pointerAddInstructionHasBounds0(_, sink1, sink2, state2)
227+
pointerAddInstructionHasBounds0(_, allocSink, sizeSink, sizeAddend)
232228
}
233229

234230
predicate isBarrier2(DataFlow::Node node, FlowState2 state) {
235-
node = Barrier2::getABarrierNode(state)
231+
node = SizeBarrier::getABarrierNode(state)
236232
}
237233

238234
predicate isBarrierIn1(DataFlow::Node node) { isSourcePair(node, _, _, _) }
@@ -245,7 +241,7 @@ private module Config implements ProductFlow::StateConfigSig {
245241
private module AllocToInvalidPointerFlow = ProductFlow::GlobalWithState<Config>;
246242

247243
/**
248-
* Holds if `pai` is non-strictly upper bounded by `sink2 + delta` and `sink1` is the
244+
* Holds if `pai` is non-strictly upper bounded by `sizeSink + delta` and `allocSink` is the
249245
* left operand of the pointer-arithmetic operation.
250246
*
251247
* For example in,
@@ -254,37 +250,37 @@ private module AllocToInvalidPointerFlow = ProductFlow::GlobalWithState<Config>;
254250
* ```
255251
* We will have:
256252
* - `pai` is `p + (size + 1)`,
257-
* - `sink1` is `p`
258-
* - `sink2` is `size`
253+
* - `allocSink` is `p`
254+
* - `sizeSink` is `size`
259255
* - `delta` is `1`.
260256
*/
261257
pragma[nomagic]
262258
private predicate pointerAddInstructionHasBounds0(
263-
PointerAddInstruction pai, DataFlow::Node sink1, DataFlow::Node sink2, int delta
259+
PointerAddInstruction pai, DataFlow::Node allocSink, DataFlow::Node sizeSink, int delta
264260
) {
265261
InterestingPointerAddInstruction::isInteresting(pragma[only_bind_into](pai)) and
266-
exists(Instruction right, Instruction instr2 |
262+
exists(Instruction right, Instruction sizeInstr |
267263
pai.getRight() = right and
268-
pai.getLeft() = sink1.asInstruction() and
269-
instr2 = sink2.asInstruction() and
270-
// pai.getRight() <= sink2 + delta
271-
bounded1(right, instr2, delta) and
272-
not right = Barrier2::getABarrierInstruction(delta) and
273-
not instr2 = Barrier2::getABarrierInstruction(delta)
264+
pai.getLeft() = allocSink.asInstruction() and
265+
sizeInstr = sizeSink.asInstruction() and
266+
// pai.getRight() <= sizeSink + delta
267+
bounded1(right, sizeInstr, delta) and
268+
not right = SizeBarrier::getABarrierInstruction(delta) and
269+
not sizeInstr = SizeBarrier::getABarrierInstruction(delta)
274270
)
275271
}
276272

277273
/**
278-
* Holds if `allocation` flows to `sink1` and `sink1` represents the left-hand
279-
* side of the pointer-arithmetic instruction `pai`, and the right-hand side of `pai`
280-
* is non-strictly upper bounded by the size of `alllocation` + `delta`.
274+
* Holds if `allocation` flows to `allocSink` and `allocSink` represents the left operand
275+
* of the pointer-arithmetic instruction `pai = a + b` (i.e., `allocSink = a`), and
276+
* `b <= allocation + delta`.
281277
*/
282278
pragma[nomagic]
283279
predicate pointerAddInstructionHasBounds(
284-
DataFlow::Node allocation, PointerAddInstruction pai, DataFlow::Node sink1, int delta
280+
DataFlow::Node allocation, PointerAddInstruction pai, DataFlow::Node allocSink, int delta
285281
) {
286-
exists(DataFlow::Node sink2 |
287-
AllocToInvalidPointerFlow::flow(allocation, _, sink1, sink2) and
288-
pointerAddInstructionHasBounds0(pai, sink1, sink2, delta)
282+
exists(DataFlow::Node sizeSink |
283+
AllocToInvalidPointerFlow::flow(allocation, _, allocSink, sizeSink) and
284+
pointerAddInstructionHasBounds0(pai, allocSink, sizeSink, delta)
289285
)
290286
}

cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/InvalidPointerToDereference.qll

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ private module InvalidPointerToDerefBarrier {
8484
}
8585

8686
additional predicate isSink(
87-
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int state, boolean testIsTrue
87+
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
8888
) {
8989
// The sink is any "large" side of a relational comparison.
90-
g.comparesLt(left.asOperand(), right.asOperand(), state, true, testIsTrue)
90+
g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue)
9191
}
9292

9393
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
@@ -105,12 +105,12 @@ private module InvalidPointerToDerefBarrier {
105105
private predicate operandGuardChecks(
106106
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
107107
) {
108-
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int state0 |
108+
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
109109
nRight.asOperand() = right and
110110
nLeft.asOperand() = left and
111-
BarrierConfig::isSink(nLeft, nRight, g, state0, edge) and
111+
BarrierConfig::isSink(nLeft, nRight, g, k, edge) and
112112
state = getInvalidPointerToDerefSourceDelta(nRight) and
113-
state0 <= state
113+
k <= state
114114
)
115115
}
116116

@@ -150,47 +150,50 @@ private module InvalidPointerToDerefConfig implements DataFlow::ConfigSig {
150150
private import DataFlow::Global<InvalidPointerToDerefConfig>
151151

152152
/**
153-
* Holds if `source1` is dataflow node that represents an allocation that flows to the
154-
* left-hand side of the pointer-arithmetic `pai`, and `derefSource` is a dataflow node with
155-
* a pointer-value that is non-strictly upper bounded by `pai + delta`.
153+
* Holds if `allocSource` is dataflow node that represents an allocation that flows to the
154+
* left-hand side of the pointer-arithmetic `pai`, and `derefSource <= pai + derefSourcePaiDelta`.
156155
*
157156
* For example, if `pai` is a pointer-arithmetic operation `p + size` in an expression such
158157
* as `(p + size) + 1` and `derefSource` is the node representing `(p + size) + 1`. In this
159-
* case `delta` is 1.
158+
* case `derefSourcePaiDelta` is 1.
160159
*/
161160
private predicate invalidPointerToDerefSource(
162-
DataFlow::Node source1, PointerArithmeticInstruction pai, DataFlow::Node derefSource, int delta
161+
DataFlow::Node allocSource, PointerArithmeticInstruction pai, DataFlow::Node derefSource,
162+
int deltaDerefSourceAndPai
163163
) {
164-
exists(int delta0 |
165-
// Note that `delta` is not necessarily equal to `delta0`:
166-
// `delta0` is the constant offset added to the size of the allocation, and
167-
// delta is the constant difference between the pointer-arithmetic instruction
164+
exists(int rhsSizeDelta |
165+
// Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`:
166+
// `rhsSizeDelta` is the constant offset added to the size of the allocation, and
167+
// `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction
168168
// and the instruction computing the address for which we will search for a dereference.
169-
AllocToInvalidPointer::pointerAddInstructionHasBounds(source1, pai, _, delta0) and
170-
bounded2(derefSource.asInstruction(), pai, delta) and
171-
delta >= 0 and
169+
AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, rhsSizeDelta) and
170+
// pai <= derefSource + deltaDerefSourceAndPai and deltaDerefSourceAndPai <= 0 is equivalent to
171+
// derefSource >= pai + deltaDerefSourceAndPai and deltaDerefSourceAndPai >= 0
172+
bounded1(pai, derefSource.asInstruction(), deltaDerefSourceAndPai) and
173+
deltaDerefSourceAndPai <= 0 and
172174
// TODO: This condition will go away once #13725 is merged, and then we can make `Barrier2`
173175
// private to `AllocationToInvalidPointer.qll`.
174-
not derefSource.getBasicBlock() = AllocToInvalidPointer::Barrier2::getABarrierBlock(delta0)
176+
not derefSource.getBasicBlock() =
177+
AllocToInvalidPointer::SizeBarrier::getABarrierBlock(rhsSizeDelta)
175178
)
176179
}
177180

178181
/**
179182
* Holds if `sink` is a sink for `InvalidPointerToDerefConfig` and `i` is a `StoreInstruction` that
180-
* writes to an address that non-strictly upper-bounds `sink`, or `i` is a `LoadInstruction` that
181-
* reads from an address that non-strictly upper-bounds `sink`.
183+
* writes to an address `addr` such that `addr <= sink`, or `i` is a `LoadInstruction` that
184+
* reads from an address `addr` such that `addr <= sink`.
182185
*/
183186
pragma[inline]
184187
private predicate isInvalidPointerDerefSink(
185-
DataFlow::Node sink, Instruction i, string operation, int delta
188+
DataFlow::Node sink, Instruction i, string operation, int deltaDerefSinkAndDerefAddress
186189
) {
187190
exists(AddressOperand addr, Instruction s, IRBlock b |
188191
s = sink.asInstruction() and
189-
bounded(addr.getDef(), s, delta) and
190-
delta >= 0 and
192+
bounded(addr.getDef(), s, deltaDerefSinkAndDerefAddress) and
193+
deltaDerefSinkAndDerefAddress >= 0 and
191194
i.getAnOperand() = addr and
192195
b = i.getBlock() and
193-
not b = InvalidPointerToDerefBarrier::getABarrierBlock(delta)
196+
not b = InvalidPointerToDerefBarrier::getABarrierBlock(deltaDerefSinkAndDerefAddress)
194197
|
195198
i instanceof StoreInstruction and
196199
operation = "write"
@@ -230,13 +233,13 @@ private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFl
230233
*/
231234
private predicate derefSinkToOperation(
232235
DataFlow::Node derefSink, PointerArithmeticInstruction pai, DataFlow::Node operation,
233-
string description, int delta
236+
string description, int deltaDerefSinkAndDerefAddress
234237
) {
235-
exists(Instruction i |
238+
exists(Instruction operationInstr |
236239
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink)) and
237-
isInvalidPointerDerefSink(derefSink, i, description, delta) and
238-
i = getASuccessor(derefSink.asInstruction()) and
239-
operation.asInstruction() = i
240+
isInvalidPointerDerefSink(derefSink, operationInstr, description, deltaDerefSinkAndDerefAddress) and
241+
operationInstr = getASuccessor(derefSink.asInstruction()) and
242+
operation.asInstruction() = operationInstr
240243
)
241244
}
242245

cpp/ql/lib/semmle/code/cpp/security/InvalidPointerDereference/RangeAnalysisUtil.qll

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,5 @@ bindingset[i]
3535
pragma[inline_late]
3636
predicate bounded1(Instruction i, Instruction b, int delta) { boundedImpl(i, b, delta) }
3737

38-
/**
39-
* Holds if `i <= b + delta`.
40-
*
41-
* This predicate enforces a join-order that ensures that `b` has already been bound.
42-
*/
43-
bindingset[b]
44-
pragma[inline_late]
45-
predicate bounded2(Instruction i, Instruction b, int delta) { boundedImpl(i, b, delta) }
46-
4738
/** Holds if `i <= b + delta`. */
4839
predicate bounded = boundedImpl/3;

0 commit comments

Comments
 (0)