Skip to content

Commit f0a1742

Browse files
committed
C++: Fix barriers in 'AllocationToInvalidPointer.qll'.
1 parent 7c9d41d commit f0a1742

File tree

1 file changed

+59
-17
lines changed

1 file changed

+59
-17
lines changed

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

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ private module SizeBarrier {
104104
hasSize(_, source, _)
105105
}
106106

107+
/**
108+
* Holds if `left < right + k` holds if `g` evaluates to `testIsTrue`.
109+
*/
107110
additional predicate isSink(
108111
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
109112
) {
@@ -124,11 +127,11 @@ private module SizeBarrier {
124127
)
125128
}
126129

127-
private predicate operandGuardChecks(
128-
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
129-
) {
130+
/**
131+
* Holds if `small < large + state` holds if `g` evaluates to `edge`.
132+
*/
133+
private predicate operandGuardChecks(IRGuardCondition g, Operand left, int state, boolean edge) {
130134
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
131-
nRight.asOperand() = right and
132135
nLeft.asOperand() = left and
133136
SizeBarrierConfig::isSink(nLeft, nRight, g, k, edge) and
134137
state = getAFlowStateForNode(nRight) and
@@ -137,32 +140,71 @@ private module SizeBarrier {
137140
}
138141

139142
/**
140-
* Gets an instruction that is guarded by a guard condition which ensures that
141-
* the value of the instruction is upper-bounded by size of some allocation.
143+
* Gets an instruction `instr` that is guarded by a check such as `instr <= left + delta` where
144+
* `left <= _ + k` and `left` is the "small side" of of a relational comparison that checks
145+
* whether `left <= size` where `size` is the size of an allocation.
142146
*/
143-
Instruction getABarrierInstruction(int state) {
147+
Instruction getABarrierInstruction0(int delta, int k) {
144148
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge |
145149
use = value.getAUse() and
146-
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _,
147-
pragma[only_bind_into](state), pragma[only_bind_into](edge)) and
148-
result = value.getAnInstruction() and
150+
// We know:
151+
// 1. result <= value + delta (by `bounded`)
152+
// 2. value < size + k + 1 (by `operandGuardChecks`).
153+
// Condition 2 implies: value <= size + k, so if we know
154+
// that `state >= k + delta` then we have:
155+
// result <= value + delta (by 1.)
156+
// <= size + k + delta (by 2.)
157+
// <= size + state (by the assumption).
158+
// Callers of `getABarrierInstruction0` should ensure that `state >= k + delta`
159+
// is satisfied.
160+
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use),
161+
pragma[only_bind_into](k + 1), pragma[only_bind_into](edge)) and
162+
bounded(result, value.getAnInstruction(), delta) and
149163
g.controls(result.getBlock(), edge)
150164
)
151165
}
152166

153167
/**
154-
* Gets a `DataFlow::Node` that is guarded by a guard condition which ensures that
155-
* the value of the node is upper-bounded by size of some allocation.
168+
* Gets an instruction that is guarded by a guard condition which ensures that
169+
* the value of the instruction is upper-bounded by size of some allocation.
156170
*/
157-
DataFlow::Node getABarrierNode(int state) {
158-
result.asOperand() = getABarrierInstruction(state).getAUse()
171+
bindingset[state]
172+
pragma[inline_late]
173+
Instruction getABarrierInstruction(int state) {
174+
exists(int delta, int k |
175+
// See the implementation comments in `getABarrierInstruction0` for why
176+
// this conjunct is necessary.
177+
state >= k + delta and
178+
result = getABarrierInstruction0(delta, k)
179+
)
180+
}
181+
182+
private module ValidForStateConfig implements DataFlow::ConfigSig {
183+
predicate isSource(DataFlow::Node source) { hasSize(_, source, _) }
184+
185+
predicate isSink(DataFlow::Node sink) { isSink(sink, _, _) }
186+
187+
additional predicate isSink(DataFlow::Node sink, int delta, int k) {
188+
sink.asOperand() = SizeBarrier::getABarrierInstruction0(delta, k).getAUse()
189+
}
159190
}
160191

192+
private module ValidForStateFlow = DataFlow::Global<ValidForStateConfig>;
193+
161194
/**
162-
* Gets the block of a node that is guarded (see `getABarrierInstruction` or
163-
* `getABarrierNode` for the definition of what it means to be guarded).
195+
* Gets a `DataFlow::Node` that is guarded by a guard condition which ensures that
196+
* the value of the node is upper-bounded by size of some allocation.
164197
*/
165-
IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) }
198+
DataFlow::Node getABarrierNode(int state) {
199+
exists(DataFlow::Node source, int delta, int k |
200+
ValidForStateFlow::flow(source, result) and
201+
hasSize(_, source, state) and
202+
ValidForStateConfig::isSink(result, delta, k) and
203+
// See the implementation comments in `getABarrierInstruction0` for why
204+
// this conjunct is necessary.
205+
state >= k + delta
206+
)
207+
}
166208
}
167209

168210
private module InterestingPointerAddInstruction {

0 commit comments

Comments
 (0)