Skip to content

Commit e4126ae

Browse files
committed
Merge branch 'main' into implement-is-unreachable-in-call-2
2 parents 655f1fe + e428502 commit e4126ae

File tree

77 files changed

+3348
-1467
lines changed

Some content is hidden

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

77 files changed

+3348
-1467
lines changed

.github/workflows/check-implicit-this.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
name: "Check implicit this warnings"
22

3-
on: workflow_dispatch
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
paths:
7+
- "**qlpack.yml"
8+
branches:
9+
- main
10+
- "rc/*"
411

512
jobs:
613
check:
@@ -15,7 +22,7 @@ jobs:
1522
for pack_file in ${packs}; do
1623
option="$(yq '.warnOnImplicitThis' ${pack_file})"
1724
if [ "${option}" != "true" ]; then
18-
echo "warnOnImplicitThis property must be set to 'true' for pack ${pack_file}"
25+
echo "::error file=${pack_file}::warnOnImplicitThis property must be set to 'true' for pack ${pack_file}"
1926
EXIT_CODE=1
2027
fi
2128
done

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,11 @@ private class PrimaryArgumentNode extends ArgumentNode, OperandNode {
321321

322322
private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode {
323323
override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) {
324-
this.getCallInstruction() = dfCall and
325-
pos.(IndirectionPosition).getArgumentIndex() = this.getArgumentIndex() and
326-
super.hasAddressOperandAndIndirectionIndex(_, pos.(IndirectionPosition).getIndirectionIndex())
324+
exists(int indirectionIndex |
325+
pos = TIndirectionPosition(argumentIndex, pragma[only_bind_into](indirectionIndex)) and
326+
this.getCallInstruction() = dfCall and
327+
super.hasAddressOperandAndIndirectionIndex(_, pragma[only_bind_into](indirectionIndex))
328+
)
327329
}
328330
}
329331

cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -312,11 +312,13 @@ Instruction getASuccessor(Instruction instr) {
312312
*/
313313
pragma[inline]
314314
predicate isInvalidPointerDerefSink(DataFlow::Node sink, Instruction i, string operation, int delta) {
315-
exists(AddressOperand addr, Instruction s |
315+
exists(AddressOperand addr, Instruction s, IRBlock b |
316316
s = sink.asInstruction() and
317-
bounded1(addr.getDef(), s, delta) and
317+
boundedImpl(addr.getDef(), s, delta) and
318318
delta >= 0 and
319-
i.getAnOperand() = addr
319+
i.getAnOperand() = addr and
320+
b = i.getBlock() and
321+
not b = InvalidPointerToDerefBarrier::getABarrierBlock(delta)
320322
|
321323
i instanceof StoreInstruction and
322324
operation = "write"
@@ -326,6 +328,60 @@ predicate isInvalidPointerDerefSink(DataFlow::Node sink, Instruction i, string o
326328
)
327329
}
328330

331+
module InvalidPointerToDerefBarrier {
332+
private module BarrierConfig implements DataFlow::ConfigSig {
333+
predicate isSource(DataFlow::Node source) {
334+
// The sources is the same as in the sources for `InvalidPointerToDerefConfig`.
335+
invalidPointerToDerefSource(_, source, _)
336+
}
337+
338+
additional predicate isSink(
339+
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int state, boolean testIsTrue
340+
) {
341+
// The sink is any "large" side of a relational comparison.
342+
g.comparesLt(left.asOperand(), right.asOperand(), state, true, testIsTrue)
343+
}
344+
345+
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
346+
}
347+
348+
private import DataFlow::Global<BarrierConfig>
349+
350+
private int getInvalidPointerToDerefSourceDelta(DataFlow::Node node) {
351+
exists(DataFlow::Node source |
352+
flow(source, node) and
353+
invalidPointerToDerefSource(_, source, result)
354+
)
355+
}
356+
357+
private predicate operandGuardChecks(
358+
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
359+
) {
360+
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int state0 |
361+
nRight.asOperand() = right and
362+
nLeft.asOperand() = left and
363+
BarrierConfig::isSink(nLeft, nRight, g, state0, edge) and
364+
state = getInvalidPointerToDerefSourceDelta(nRight) and
365+
state0 <= state
366+
)
367+
}
368+
369+
Instruction getABarrierInstruction(int state) {
370+
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge |
371+
use = value.getAUse() and
372+
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _, state,
373+
pragma[only_bind_into](edge)) and
374+
result = value.getAnInstruction() and
375+
g.controls(result.getBlock(), edge)
376+
)
377+
}
378+
379+
DataFlow::Node getABarrierNode() { result.asOperand() = getABarrierInstruction(_).getAUse() }
380+
381+
pragma[nomagic]
382+
IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) }
383+
}
384+
329385
/**
330386
* A configuration to track flow from a pointer-arithmetic operation found
331387
* by `AllocToInvalidPointerConfig` to a dereference of the pointer.
@@ -338,6 +394,8 @@ module InvalidPointerToDerefConfig implements DataFlow::ConfigSig {
338394

339395
predicate isBarrier(DataFlow::Node node) {
340396
node = any(DataFlow::SsaPhiNode phi | not phi.isPhiRead()).getAnInput(true)
397+
or
398+
node = InvalidPointerToDerefBarrier::getABarrierNode()
341399
}
342400
}
343401

@@ -382,7 +440,7 @@ newtype TMergedPathNode =
382440
// pointer, but we want to raise an alert at the dereference.
383441
TPathNodeSink(Instruction i) {
384442
exists(DataFlow::Node n |
385-
InvalidPointerToDerefFlow::flowTo(n) and
443+
InvalidPointerToDerefFlow::flowTo(pragma[only_bind_into](n)) and
386444
isInvalidPointerDerefSink(n, i, _, _) and
387445
i = getASuccessor(n.asInstruction())
388446
)

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@ edges
3939
| test.cpp:148:23:148:28 | buffer | test.cpp:151:5:151:11 | access to array |
4040
| test.cpp:159:25:159:29 | array | test.cpp:161:5:161:10 | access to array |
4141
| test.cpp:159:25:159:29 | array | test.cpp:162:5:162:10 | access to array |
42+
| test.cpp:175:30:175:30 | p | test.cpp:191:27:191:30 | access to array |
43+
| test.cpp:198:14:198:20 | buffer1 | test.cpp:175:30:175:30 | p |
44+
| test.cpp:198:14:198:20 | buffer1 | test.cpp:198:14:198:20 | buffer1 |
45+
| test.cpp:201:14:201:20 | buffer2 | test.cpp:175:30:175:30 | p |
46+
| test.cpp:201:14:201:20 | buffer2 | test.cpp:201:14:201:20 | buffer2 |
47+
| test.cpp:204:14:204:20 | buffer3 | test.cpp:175:30:175:30 | p |
48+
| test.cpp:204:14:204:20 | buffer3 | test.cpp:204:14:204:20 | buffer3 |
49+
| test.cpp:207:35:207:35 | p | test.cpp:208:14:208:14 | p |
50+
| test.cpp:208:14:208:14 | p | test.cpp:175:30:175:30 | p |
51+
| test.cpp:213:19:213:25 | buffer1 | test.cpp:207:35:207:35 | p |
52+
| test.cpp:213:19:213:25 | buffer1 | test.cpp:213:19:213:25 | buffer1 |
53+
| test.cpp:216:19:216:25 | buffer2 | test.cpp:207:35:207:35 | p |
54+
| test.cpp:216:19:216:25 | buffer2 | test.cpp:216:19:216:25 | buffer2 |
55+
| test.cpp:219:19:219:25 | buffer3 | test.cpp:207:35:207:35 | p |
56+
| test.cpp:219:19:219:25 | buffer3 | test.cpp:219:19:219:25 | buffer3 |
4257
nodes
4358
| test.cpp:34:5:34:24 | access to array | semmle.label | access to array |
4459
| test.cpp:34:10:34:12 | buf | semmle.label | buf |
@@ -97,6 +112,22 @@ nodes
97112
| test.cpp:159:25:159:29 | array | semmle.label | array |
98113
| test.cpp:161:5:161:10 | access to array | semmle.label | access to array |
99114
| test.cpp:162:5:162:10 | access to array | semmle.label | access to array |
115+
| test.cpp:175:30:175:30 | p | semmle.label | p |
116+
| test.cpp:191:27:191:30 | access to array | semmle.label | access to array |
117+
| test.cpp:198:14:198:20 | buffer1 | semmle.label | buffer1 |
118+
| test.cpp:198:14:198:20 | buffer1 | semmle.label | buffer1 |
119+
| test.cpp:201:14:201:20 | buffer2 | semmle.label | buffer2 |
120+
| test.cpp:201:14:201:20 | buffer2 | semmle.label | buffer2 |
121+
| test.cpp:204:14:204:20 | buffer3 | semmle.label | buffer3 |
122+
| test.cpp:204:14:204:20 | buffer3 | semmle.label | buffer3 |
123+
| test.cpp:207:35:207:35 | p | semmle.label | p |
124+
| test.cpp:208:14:208:14 | p | semmle.label | p |
125+
| test.cpp:213:19:213:25 | buffer1 | semmle.label | buffer1 |
126+
| test.cpp:213:19:213:25 | buffer1 | semmle.label | buffer1 |
127+
| test.cpp:216:19:216:25 | buffer2 | semmle.label | buffer2 |
128+
| test.cpp:216:19:216:25 | buffer2 | semmle.label | buffer2 |
129+
| test.cpp:219:19:219:25 | buffer3 | semmle.label | buffer3 |
130+
| test.cpp:219:19:219:25 | buffer3 | semmle.label | buffer3 |
100131
subpaths
101132
#select
102133
| test.cpp:35:5:35:22 | PointerAdd: access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write |
@@ -113,3 +144,5 @@ subpaths
113144
| test.cpp:136:9:136:16 | PointerAdd: ... += ... | test.cpp:143:18:143:21 | asdf | test.cpp:138:13:138:15 | arr | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:142:10:142:13 | asdf | asdf | test.cpp:138:12:138:15 | Load: * ... | read |
114145
| test.cpp:151:5:151:11 | PointerAdd: access to array | test.cpp:148:23:148:28 | buffer | test.cpp:151:5:151:11 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:147:19:147:24 | buffer | buffer | test.cpp:151:5:151:15 | Store: ... = ... | write |
115146
| test.cpp:162:5:162:10 | PointerAdd: access to array | test.cpp:159:25:159:29 | array | test.cpp:162:5:162:10 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:158:10:158:14 | array | array | test.cpp:162:5:162:19 | Store: ... = ... | write |
147+
| test.cpp:191:27:191:30 | PointerAdd: access to array | test.cpp:201:14:201:20 | buffer2 | test.cpp:191:27:191:30 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:200:19:200:25 | buffer2 | buffer2 | test.cpp:191:27:191:30 | Load: access to array | read |
148+
| test.cpp:191:27:191:30 | PointerAdd: access to array | test.cpp:216:19:216:25 | buffer2 | test.cpp:191:27:191:30 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:215:19:215:25 | buffer2 | buffer2 | test.cpp:191:27:191:30 | Load: access to array | read |

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,52 @@ void pointer_size_larger_than_array_element_size_and_does_not_divide_it() {
169169
ptr[0] = vec3{}; // GOOD: writes ints 0, 1, 2
170170
ptr[1] = vec3{}; // BAD: writes ints 3, 4, 5 [NOT DETECTED]
171171
}
172+
173+
void use(...);
174+
175+
void call_use(unsigned char* p, int n) {
176+
if(n == 0) {
177+
return;
178+
}
179+
if(n == 1) {
180+
unsigned char x = p[0];
181+
use(x);
182+
}
183+
if(n == 2) {
184+
unsigned char x = p[0];
185+
unsigned char y = p[1];
186+
use(x, y);
187+
}
188+
if(n == 3) {
189+
unsigned char x = p[0];
190+
unsigned char y = p[1];
191+
unsigned char z = p[2]; // GOOD [FALSE POSITIVE]: `call_use(buffer2, 2)` won't reach this point.
192+
use(x, y, z);
193+
}
194+
}
195+
196+
void test_call_use() {
197+
unsigned char buffer1[1];
198+
call_use(buffer1,1);
199+
200+
unsigned char buffer2[2];
201+
call_use(buffer2,2);
202+
203+
unsigned char buffer3[3];
204+
call_use(buffer3,3);
205+
}
206+
207+
void call_call_use(unsigned char* p, int n) {
208+
call_use(p, n);
209+
}
210+
211+
void test_call_use2() {
212+
unsigned char buffer1[1];
213+
call_call_use(buffer1,1);
214+
215+
unsigned char buffer2[2];
216+
call_call_use(buffer2,2);
217+
218+
unsigned char buffer3[3];
219+
call_call_use(buffer3,3);
220+
}

0 commit comments

Comments
 (0)