Skip to content

Commit 786c6e1

Browse files
committed
Merge branch 'main' into swap-argument-order-in-invalid-ptr-deref
2 parents 742f080 + 4676ca5 commit 786c6e1

File tree

4 files changed

+80
-83
lines changed

4 files changed

+80
-83
lines changed

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

Lines changed: 45 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -42,59 +42,57 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
4242
* ```
4343
* In this case, the sink pair identified by the product flow library (without any additional barriers)
4444
* would be `(p, n)` (where `n` is the `n` in `p[n]`), because there exists a pointer-arithmetic
45-
* instruction `pai` such that:
46-
* 1. The left-hand of `pai` flows from the allocation, and
47-
* 2. The right-hand of `pai` is non-strictly upper bounded by `n` (where `n` is the `n` in `p[n]`)
45+
* instruction `pai = a + b` such that:
46+
* 1. the allocation flows to `a`, and
47+
* 2. `b <= n` where `n` is the `n` in `p[n]`
4848
* but because there's a strict comparison that compares `n` against the size of the allocation this
4949
* snippet is fine.
5050
*/
51-
module Barrier2 {
52-
private class FlowState2 = int;
53-
54-
private module BarrierConfig2 implements DataFlow::ConfigSig {
51+
module SizeBarrier {
52+
private module SizeBarrierConfig implements DataFlow::ConfigSig {
5553
predicate isSource(DataFlow::Node source) {
5654
// The sources is the same as in the sources for the second
5755
// projection in the `AllocToInvalidPointerConfig` module.
5856
hasSize(_, source, _)
5957
}
6058

6159
additional predicate isSink(
62-
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, FlowState2 state,
63-
boolean testIsTrue
60+
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
6461
) {
65-
// The sink is any "large" side of a relational comparison.
66-
g.comparesLt(left.asOperand(), right.asOperand(), state, true, testIsTrue)
62+
// The sink is any "large" side of a relational comparison. i.e., the `right` expression
63+
// in a guard such as `left < right + k`.
64+
g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue)
6765
}
6866

6967
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
7068
}
7169

72-
private import DataFlow::Global<BarrierConfig2>
70+
private import DataFlow::Global<SizeBarrierConfig>
7371

74-
private FlowState2 getAFlowStateForNode(DataFlow::Node node) {
72+
private int getAFlowStateForNode(DataFlow::Node node) {
7573
exists(DataFlow::Node source |
7674
flow(source, node) and
7775
hasSize(_, source, result)
7876
)
7977
}
8078

8179
private predicate operandGuardChecks(
82-
IRGuardCondition g, Operand left, Operand right, FlowState2 state, boolean edge
80+
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
8381
) {
84-
exists(DataFlow::Node nLeft, DataFlow::Node nRight, FlowState2 state0 |
82+
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
8583
nRight.asOperand() = right and
8684
nLeft.asOperand() = left and
87-
BarrierConfig2::isSink(nLeft, nRight, g, state0, edge) and
85+
SizeBarrierConfig::isSink(nLeft, nRight, g, k, edge) and
8886
state = getAFlowStateForNode(nRight) and
89-
state0 <= state
87+
k <= state
9088
)
9189
}
9290

9391
/**
9492
* Gets an instruction that is guarded by a guard condition which ensures that
9593
* the value of the instruction is upper-bounded by size of some allocation.
9694
*/
97-
Instruction getABarrierInstruction(FlowState2 state) {
95+
Instruction getABarrierInstruction(int state) {
9896
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge |
9997
use = value.getAUse() and
10098
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _,
@@ -108,17 +106,15 @@ module Barrier2 {
108106
* Gets a `DataFlow::Node` that is guarded by a guard condition which ensures that
109107
* the value of the node is upper-bounded by size of some allocation.
110108
*/
111-
DataFlow::Node getABarrierNode(FlowState2 state) {
109+
DataFlow::Node getABarrierNode(int state) {
112110
result.asOperand() = getABarrierInstruction(state).getAUse()
113111
}
114112

115113
/**
116114
* Gets the block of a node that is guarded (see `getABarrierInstruction` or
117115
* `getABarrierNode` for the definition of what it means to be guarded).
118116
*/
119-
IRBlock getABarrierBlock(FlowState2 state) {
120-
result.getAnInstruction() = getABarrierInstruction(state)
121-
}
117+
IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) }
122118
}
123119

124120
private module InterestingPointerAddInstruction {
@@ -151,8 +147,8 @@ private module InterestingPointerAddInstruction {
151147
}
152148

153149
/**
154-
* A product-flow configuration for flow from an (allocation, size) pair to a
155-
* pointer-arithmetic operation that is non-strictly upper-bounded by `allocation + size`.
150+
* A product-flow configuration for flow from an `(allocation, size)` pair to a
151+
* pointer-arithmetic operation `pai` such that `pai <= allocation + size`.
156152
*
157153
* The goal of this query is to find patterns such as:
158154
* ```cpp
@@ -176,29 +172,29 @@ private module Config implements ProductFlow::StateConfigSig {
176172
class FlowState2 = int;
177173

178174
predicate isSourcePair(
179-
DataFlow::Node source1, FlowState1 state1, DataFlow::Node source2, FlowState2 state2
175+
DataFlow::Node allocSource, FlowState1 unit, DataFlow::Node sizeSource, FlowState2 sizeAddend
180176
) {
181177
// In the case of an allocation like
182178
// ```cpp
183179
// malloc(size + 1);
184180
// ```
185181
// we use `state2` to remember that there was an offset (in this case an offset of `1`) added
186182
// to the size of the allocation. This state is then checked in `isSinkPair`.
187-
exists(state1) and
188-
hasSize(source1.asConvertedExpr(), source2, state2)
183+
exists(unit) and
184+
hasSize(allocSource.asConvertedExpr(), sizeSource, sizeAddend)
189185
}
190186

191187
predicate isSinkPair(
192-
DataFlow::Node sink1, FlowState1 state1, DataFlow::Node sink2, FlowState2 state2
188+
DataFlow::Node allocSink, FlowState1 unit, DataFlow::Node sizeSink, FlowState2 sizeAddend
193189
) {
194-
exists(state1) and
190+
exists(unit) and
195191
// We check that the delta computed by the range analysis matches the
196192
// state value that we set in `isSourcePair`.
197-
pointerAddInstructionHasBounds0(_, sink1, sink2, state2)
193+
pointerAddInstructionHasBounds0(_, allocSink, sizeSink, sizeAddend)
198194
}
199195

200196
predicate isBarrier2(DataFlow::Node node, FlowState2 state) {
201-
node = Barrier2::getABarrierNode(state)
197+
node = SizeBarrier::getABarrierNode(state)
202198
}
203199

204200
predicate isBarrierIn1(DataFlow::Node node) { isSourcePair(node, _, _, _) }
@@ -211,7 +207,7 @@ private module Config implements ProductFlow::StateConfigSig {
211207
private module AllocToInvalidPointerFlow = ProductFlow::GlobalWithState<Config>;
212208

213209
/**
214-
* Holds if `pai` is non-strictly upper bounded by `sink2 + delta` and `sink1` is the
210+
* Holds if `pai` is non-strictly upper bounded by `sizeSink + delta` and `allocSink` is the
215211
* left operand of the pointer-arithmetic operation.
216212
*
217213
* For example in,
@@ -220,37 +216,37 @@ private module AllocToInvalidPointerFlow = ProductFlow::GlobalWithState<Config>;
220216
* ```
221217
* We will have:
222218
* - `pai` is `p + (size + 1)`,
223-
* - `sink1` is `p`
224-
* - `sink2` is `size`
219+
* - `allocSink` is `p`
220+
* - `sizeSink` is `size`
225221
* - `delta` is `1`.
226222
*/
227223
pragma[nomagic]
228224
private predicate pointerAddInstructionHasBounds0(
229-
PointerAddInstruction pai, DataFlow::Node sink1, DataFlow::Node sink2, int delta
225+
PointerAddInstruction pai, DataFlow::Node allocSink, DataFlow::Node sizeSink, int delta
230226
) {
231227
InterestingPointerAddInstruction::isInteresting(pragma[only_bind_into](pai)) and
232-
exists(Instruction right, Instruction instr2 |
228+
exists(Instruction right, Instruction sizeInstr |
233229
pai.getRight() = right and
234-
pai.getLeft() = sink1.asInstruction() and
235-
instr2 = sink2.asInstruction() and
236-
// pai.getRight() <= sink2 + delta
237-
bounded1(right, instr2, delta) and
238-
not right = Barrier2::getABarrierInstruction(delta) and
239-
not instr2 = Barrier2::getABarrierInstruction(delta)
230+
pai.getLeft() = allocSink.asInstruction() and
231+
sizeInstr = sizeSink.asInstruction() and
232+
// pai.getRight() <= sizeSink + delta
233+
bounded1(right, sizeInstr, delta) and
234+
not right = SizeBarrier::getABarrierInstruction(delta) and
235+
not sizeInstr = SizeBarrier::getABarrierInstruction(delta)
240236
)
241237
}
242238

243239
/**
244-
* Holds if `allocation` flows to `sink1` and `sink1` represents the left-hand
245-
* side of the pointer-arithmetic instruction `pai`, and the right-hand side of `pai`
246-
* is non-strictly upper bounded by the size of `alllocation` + `delta`.
240+
* Holds if `allocation` flows to `allocSink` and `allocSink` represents the left operand
241+
* of the pointer-arithmetic instruction `pai = a + b` (i.e., `allocSink = a`), and
242+
* `b <= allocation + delta`.
247243
*/
248244
pragma[nomagic]
249245
predicate pointerAddInstructionHasBounds(
250-
DataFlow::Node allocation, PointerAddInstruction pai, DataFlow::Node sink1, int delta
246+
DataFlow::Node allocation, PointerAddInstruction pai, DataFlow::Node allocSink, int delta
251247
) {
252-
exists(DataFlow::Node sink2 |
253-
AllocToInvalidPointerFlow::flow(allocation, _, sink1, sink2) and
254-
pointerAddInstructionHasBounds0(pai, sink1, sink2, delta)
248+
exists(DataFlow::Node sizeSink |
249+
AllocToInvalidPointerFlow::flow(allocation, _, allocSink, sizeSink) and
250+
pointerAddInstructionHasBounds0(pai, allocSink, sizeSink, delta)
255251
)
256252
}

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

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ private module InvalidPointerToDerefBarrier {
1919
}
2020

2121
additional predicate isSink(
22-
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int state, boolean testIsTrue
22+
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
2323
) {
2424
// The sink is any "large" side of a relational comparison.
25-
g.comparesLt(left.asOperand(), right.asOperand(), state, true, testIsTrue)
25+
g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue)
2626
}
2727

2828
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
@@ -40,12 +40,12 @@ private module InvalidPointerToDerefBarrier {
4040
private predicate operandGuardChecks(
4141
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
4242
) {
43-
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int state0 |
43+
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
4444
nRight.asOperand() = right and
4545
nLeft.asOperand() = left and
46-
BarrierConfig::isSink(nLeft, nRight, g, state0, edge) and
46+
BarrierConfig::isSink(nLeft, nRight, g, k, edge) and
4747
state = getInvalidPointerToDerefSourceDelta(nRight) and
48-
state0 <= state
48+
k <= state
4949
)
5050
}
5151

@@ -85,49 +85,50 @@ private module InvalidPointerToDerefConfig implements DataFlow::ConfigSig {
8585
private import DataFlow::Global<InvalidPointerToDerefConfig>
8686

8787
/**
88-
* Holds if `source1` is dataflow node that represents an allocation that flows to the
89-
* left-hand side of the pointer-arithmetic `pai`, and `derefSource` is a dataflow node with
90-
* a pointer-value that is non-strictly upper bounded by `pai + delta`.
88+
* Holds if `allocSource` is dataflow node that represents an allocation that flows to the
89+
* left-hand side of the pointer-arithmetic `pai`, and `derefSource <= pai + derefSourcePaiDelta`.
9190
*
9291
* For example, if `pai` is a pointer-arithmetic operation `p + size` in an expression such
9392
* as `(p + size) + 1` and `derefSource` is the node representing `(p + size) + 1`. In this
94-
* case `delta` is 1.
93+
* case `derefSourcePaiDelta` is 1.
9594
*/
9695
private predicate invalidPointerToDerefSource(
97-
DataFlow::Node source1, PointerArithmeticInstruction pai, DataFlow::Node derefSource, int delta
96+
DataFlow::Node allocSource, PointerArithmeticInstruction pai, DataFlow::Node derefSource,
97+
int deltaDerefSourceAndPai
9898
) {
99-
exists(int delta0 |
100-
// Note that `delta` is not necessarily equal to `delta0`:
101-
// `delta0` is the constant offset added to the size of the allocation, and
102-
// delta is the constant difference between the pointer-arithmetic instruction
99+
exists(int rhsSizeDelta |
100+
// Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`:
101+
// `rhsSizeDelta` is the constant offset added to the size of the allocation, and
102+
// `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction
103103
// and the instruction computing the address for which we will search for a dereference.
104-
AllocToInvalidPointer::pointerAddInstructionHasBounds(source1, pai, _, delta0) and
105-
// pai <= derefSource + delta and delta <= 0 is equivalent to
106-
// derefSource >= pai + delta and delta >= 0
107-
bounded1(pai, derefSource.asInstruction(), delta) and
108-
delta <= 0 and
104+
AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, rhsSizeDelta) and
105+
// pai <= derefSource + deltaDerefSourceAndPai and deltaDerefSourceAndPai <= 0 is equivalent to
106+
// derefSource >= pai + deltaDerefSourceAndPai and deltaDerefSourceAndPai >= 0
107+
bounded1(pai, derefSource.asInstruction(), deltaDerefSourceAndPai) and
108+
deltaDerefSourceAndPai <= 0 and
109109
// TODO: This condition will go away once #13725 is merged, and then we can make `Barrier2`
110110
// private to `AllocationToInvalidPointer.qll`.
111-
not derefSource.getBasicBlock() = AllocToInvalidPointer::Barrier2::getABarrierBlock(delta0)
111+
not derefSource.getBasicBlock() =
112+
AllocToInvalidPointer::SizeBarrier::getABarrierBlock(rhsSizeDelta)
112113
)
113114
}
114115

115116
/**
116117
* Holds if `sink` is a sink for `InvalidPointerToDerefConfig` and `i` is a `StoreInstruction` that
117-
* writes to an address that non-strictly upper-bounds `sink`, or `i` is a `LoadInstruction` that
118-
* reads from an address that non-strictly upper-bounds `sink`.
118+
* writes to an address `addr` such that `addr <= sink`, or `i` is a `LoadInstruction` that
119+
* reads from an address `addr` such that `addr <= sink`.
119120
*/
120121
pragma[inline]
121122
private predicate isInvalidPointerDerefSink(
122-
DataFlow::Node sink, Instruction i, string operation, int delta
123+
DataFlow::Node sink, Instruction i, string operation, int deltaDerefSinkAndDerefAddress
123124
) {
124125
exists(AddressOperand addr, Instruction s, IRBlock b |
125126
s = sink.asInstruction() and
126-
bounded(addr.getDef(), s, delta) and
127-
delta >= 0 and
127+
bounded(addr.getDef(), s, deltaDerefSinkAndDerefAddress) and
128+
deltaDerefSinkAndDerefAddress >= 0 and
128129
i.getAnOperand() = addr and
129130
b = i.getBlock() and
130-
not b = InvalidPointerToDerefBarrier::getABarrierBlock(delta)
131+
not b = InvalidPointerToDerefBarrier::getABarrierBlock(deltaDerefSinkAndDerefAddress)
131132
|
132133
i instanceof StoreInstruction and
133134
operation = "write"
@@ -167,13 +168,13 @@ private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFl
167168
*/
168169
private predicate derefSinkToOperation(
169170
DataFlow::Node derefSink, PointerArithmeticInstruction pai, DataFlow::Node operation,
170-
string description, int delta
171+
string description, int deltaDerefSinkAndDerefAddress
171172
) {
172-
exists(Instruction i |
173+
exists(Instruction operationInstr |
173174
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink)) and
174-
isInvalidPointerDerefSink(derefSink, i, description, delta) and
175-
i = getASuccessor(derefSink.asInstruction()) and
176-
operation.asInstruction() = i
175+
isInvalidPointerDerefSink(derefSink, operationInstr, description, deltaDerefSinkAndDerefAddress) and
176+
operationInstr = getASuccessor(derefSink.asInstruction()) and
177+
operation.asInstruction() = operationInstr
177178
)
178179
}
179180

java/documentation/library-coverage/coverage.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,
5555
jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,94,55
5656
java.awt,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3
5757
java.beans,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
58-
java.io,50,,45,,,22,,,,,,,,,,,,,,28,,,,,,,,,,,,,,,,,,,43,2
58+
java.io,50,,46,,,22,,,,,,,,,,,,,,28,,,,,,,,,,,,,,,,,,,44,2
5959
java.lang,31,,93,,13,,,,,,,,,,,,8,,,5,,,4,,,1,,,,,,,,,,,,,57,36
6060
java.net,13,3,23,,,,,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,3,23,
6161
java.nio,53,,36,,,5,,,,,,,,,,,,,,47,,,,,,,,,1,,,,,,,,,,36,

java/documentation/library-coverage/coverage.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ Java framework & library support
1818
`Google Guava <https://guava.dev/>`_,``com.google.common.*``,,730,41,7,,,,,
1919
JBoss Logging,``org.jboss.logging``,,,324,,,,,,
2020
`JSON-java <https://github.com/stleary/JSON-java>`_,``org.json``,,236,,,,,,,
21-
Java Standard Library,``java.*``,3,688,205,80,,9,,,18
21+
Java Standard Library,``java.*``,3,689,205,80,,9,,,18
2222
Java extensions,"``javax.*``, ``jakarta.*``",63,672,34,2,4,,1,1,2
2323
Kotlin Standard Library,``kotlin*``,,1849,16,14,,,,,2
2424
`Spring <https://spring.io/>`_,``org.springframework.*``,29,483,115,4,,28,14,,35
2525
Others,"``antlr``, ``cn.hutool.core.codec``, ``com.alibaba.druid.sql``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.google.gson``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.thoughtworks.xstream``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.text``, ``groovy.util``, ``hudson``, ``io.jsonwebtoken``, ``io.netty.bootstrap``, ``io.netty.buffer``, ``io.netty.channel``, ``io.netty.handler.codec``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.cargo.container.installer``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.eclipse.jetty.client``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.gradle.api.file``, ``org.hibernate``, ``org.influxdb``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.jooq``, ``org.kohsuke.stapler``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``org.yaml.snakeyaml``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",126,5237,577,89,6,18,18,,200
26-
Totals,,283,13606,2067,290,16,122,33,1,391
26+
Totals,,283,13607,2067,290,16,122,33,1,391
2727

0 commit comments

Comments
 (0)