Skip to content

Commit 70837db

Browse files
committed
C++: Use range analysis to properly deduce the initial 'state2' instead of traversing the AST. Also fix state-passing related to negative states.
1 parent 6537c81 commit 70837db

File tree

1 file changed

+23
-46
lines changed

1 file changed

+23
-46
lines changed

cpp/ql/src/experimental/Likely Bugs/OverrunWriteProductFlow.ql

Lines changed: 23 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -39,59 +39,35 @@ predicate bounded(Instruction i, Instruction b, int delta) {
3939
)
4040
}
4141

42-
/**
43-
* Holds if the combination of `n` and `state` represents an appropriate
44-
* source for the expression `e` suitable for use-use flow.
45-
*/
46-
private predicate hasSizeImpl(Expr e, DataFlow::Node n, string state) {
47-
// The simple case: If the size is a variable access with no qualifier we can just use the
48-
// dataflow node for that expression and no state.
49-
exists(VariableAccess va |
50-
va = e and
51-
not va instanceof FieldAccess and
52-
n.asConvertedExpr() = va.getFullyConverted() and
53-
state = "0"
54-
)
55-
or
56-
// If the size is a choice between two expressions we allow both to be nodes representing the size.
57-
exists(ConditionalExpr cond | cond = e | hasSizeImpl([cond.getThen(), cond.getElse()], n, state))
58-
or
59-
// If the size is an expression plus a constant, we pick the dataflow node of the expression and
60-
// remember the constant in the state.
61-
exists(Expr const, Expr nonconst |
62-
e.(AddExpr).hasOperands(const, nonconst) and
63-
state = const.getValue() and
64-
hasSizeImpl(nonconst, n, _)
65-
)
66-
or
67-
exists(Expr const, Expr nonconst |
68-
e.(SubExpr).hasOperands(const, nonconst) and
69-
state = "-" + const.getValue() and
70-
hasSizeImpl(nonconst, n, _)
71-
)
72-
}
42+
VariableAccess getAVariableAccess(Expr e) { e.getAChild*() = result }
7343

7444
/**
7545
* Holds if `(n, state)` pair represents the source of flow for the size
7646
* expression associated with `alloc`.
7747
*/
7848
predicate hasSize(AllocationExpr alloc, DataFlow::Node n, string state) {
79-
hasSizeImpl(alloc.getSizeExpr(), n, state)
49+
exists(VariableAccess va, Expr size, int delta |
50+
size = alloc.getSizeExpr() and
51+
// Get the unique variable in a size expression like `x` in `malloc(x + 1)`.
52+
va = unique( | | getAVariableAccess(size)) and
53+
// Compute `delta` as the constant difference between `x` and `x + 1`.
54+
bounded(any(Instruction instr | instr.getUnconvertedResultExpression() = size),
55+
any(LoadInstruction load | load.getUnconvertedResultExpression() = va), delta) and
56+
n.asConvertedExpr() = va.getFullyConverted() and
57+
state = delta.toString()
58+
)
8059
}
8160

8261
predicate isSinkPairImpl(
83-
CallInstruction c, DataFlow::Node bufSink, DataFlow::Node sizeSink, int delta, Expr eBuf,
84-
Expr eSize
62+
CallInstruction c, DataFlow::Node bufSink, DataFlow::Node sizeSink, int delta, Expr eBuf
8563
) {
8664
exists(int bufIndex, int sizeIndex, Instruction sizeInstr, Instruction bufInstr |
8765
bufInstr = bufSink.asInstruction() and
8866
c.getArgument(bufIndex) = bufInstr and
8967
sizeInstr = sizeSink.asInstruction() and
9068
c.getStaticCallTarget().(ArrayFunction).hasArrayWithVariableSize(bufIndex, sizeIndex) and
9169
bounded(c.getArgument(sizeIndex), sizeInstr, delta) and
92-
eSize = sizeInstr.getUnconvertedResultExpression() and
93-
eBuf = bufInstr.getUnconvertedResultExpression() and
94-
delta >= 1
70+
eBuf = bufInstr.getUnconvertedResultExpression()
9571
)
9672
}
9773

@@ -117,9 +93,9 @@ class StringSizeConfiguration extends ProductFlow::Configuration {
11793
DataFlow::FlowState state2
11894
) {
11995
state1 instanceof DataFlow::FlowStateEmpty and
120-
state2 = [0 .. 32].toString() and // An arbitrary bound because we need to bound `state2`
96+
state2 = [-32 .. 32].toString() and // An arbitrary bound because we need to bound `state2`
12197
exists(int delta |
122-
isSinkPairImpl(_, bufSink, sizeSink, delta, _, _) and
98+
isSinkPairImpl(_, bufSink, sizeSink, delta, _) and
12399
delta > state2.toInt()
124100
)
125101
}
@@ -129,7 +105,7 @@ class StringSizeConfiguration extends ProductFlow::Configuration {
129105
DataFlow::FlowState state2
130106
) {
131107
exists(AddInstruction add, Operand op, int delta, int s1, int s2 |
132-
s1 = [0 .. 32] and // An arbitrary bound because we need to bound `state`
108+
s1 = [-32 .. 32] and // An arbitrary bound because we need to bound `state`
133109
state1 = s1.toString() and
134110
state2 = s2.toString() and
135111
add.hasOperands(node1.asOperand(), op) and
@@ -142,13 +118,14 @@ class StringSizeConfiguration extends ProductFlow::Configuration {
142118

143119
from
144120
StringSizeConfiguration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2,
145-
DataFlow::PathNode sink1, DataFlow2::PathNode sink2, int k, CallInstruction c,
146-
DataFlow::Node sourceNode, Expr bound, Expr buffer, string element
121+
DataFlow::PathNode sink1, DataFlow2::PathNode sink2, int overflow, int sinkState,
122+
CallInstruction c, DataFlow::Node sourceNode, Expr buffer, string element
147123
where
148124
conf.hasFlowPath(source1, source2, sink1, sink2) and
149-
k > sink2.getState().toInt() and
150-
isSinkPairImpl(c, sink1.getNode(), sink2.getNode(), k, buffer, bound) and
125+
sinkState = sink2.getState().toInt() and
126+
isSinkPairImpl(c, sink1.getNode(), sink2.getNode(), overflow + sinkState, buffer) and
127+
overflow > 0 and
151128
sourceNode = source1.getNode() and
152-
if k = 1 then element = " element." else element = " elements."
129+
if overflow = 1 then element = " element." else element = " elements."
153130
select c.getUnconvertedResultExpression(), source1, sink1,
154-
"This write may overflow $@ by " + k + element, buffer, buffer.toString()
131+
"This write may overflow $@ by " + overflow + element, buffer, buffer.toString()

0 commit comments

Comments
 (0)