Skip to content

Commit 576ea32

Browse files
committed
Merge remote-tracking branch 'origin/main' into criemen/bazel-csharp
2 parents 24a14ba + 11da42b commit 576ea32

File tree

276 files changed

+6664
-6062
lines changed

Some content is hidden

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

276 files changed

+6664
-6062
lines changed

cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll

Lines changed: 104 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ class IRGuardCondition extends Instruction {
565565
/** Holds if (determined by this guard) `op == k` evaluates to `areEqual` if this expression evaluates to `value`. */
566566
cached
567567
predicate comparesEq(Operand op, int k, boolean areEqual, AbstractValue value) {
568-
compares_eq(this, op, k, areEqual, value)
568+
unary_compares_eq(this, op, k, areEqual, false, value)
569569
}
570570

571571
/**
@@ -586,7 +586,7 @@ class IRGuardCondition extends Instruction {
586586
cached
587587
predicate ensuresEq(Operand op, int k, IRBlock block, boolean areEqual) {
588588
exists(AbstractValue value |
589-
compares_eq(this, op, k, areEqual, value) and this.valueControls(block, value)
589+
unary_compares_eq(this, op, k, areEqual, false, value) and this.valueControls(block, value)
590590
)
591591
}
592592

@@ -611,7 +611,7 @@ class IRGuardCondition extends Instruction {
611611
cached
612612
predicate ensuresEqEdge(Operand op, int k, IRBlock pred, IRBlock succ, boolean areEqual) {
613613
exists(AbstractValue value |
614-
compares_eq(this, op, k, areEqual, value) and
614+
unary_compares_eq(this, op, k, areEqual, false, value) and
615615
this.valueControlsEdge(pred, succ, value)
616616
)
617617
}
@@ -737,26 +737,66 @@ private predicate compares_eq(
737737
)
738738
}
739739

740-
/** Holds if `op == k` is `areEqual` given that `test` is equal to `value`. */
741-
private predicate compares_eq(
742-
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
740+
/**
741+
* Holds if `op == k` is `areEqual` given that `test` is equal to `value`.
742+
*
743+
* Many internal predicates in this file have a `inNonZeroCase` column.
744+
* Ideally, the `k` column would be a type such as `Option<int>::Option`, to
745+
* represent whether we have a concrete value `k` such that `op == k`, or whether
746+
* we only know that `op != 0`.
747+
* However, cannot instantiate `Option` with an infinite type. Thus the boolean
748+
* `inNonZeroCase` is used to distinquish the `Some` (where we have a concrete
749+
* value `k`) and `None` cases (where we only know that `op != 0`).
750+
*
751+
* Thus, if `inNonZeroCase = true` then `op != 0` and the value of `k` is
752+
* meaningless.
753+
*
754+
* To see why `inNonZeroCase` is needed consider the following C program:
755+
* ```c
756+
* char* p = ...;
757+
* if(p) {
758+
* use(p);
759+
* }
760+
* ```
761+
* in C++ there would be an int-to-bool conversion on `p`. However, since C
762+
* does not have booleans there is no conversion. We want to be able to
763+
* conclude that `p` is non-zero in the true branch, so we need to give `k`
764+
* some value. However, simply setting `k = 1` would make the rest of the
765+
* analysis think that `k == 1` holds inside the branch. So we distinquish
766+
* between the above case and
767+
* ```c
768+
* if(p == 1) {
769+
* use(p)
770+
* }
771+
* ```
772+
* by setting `inNonZeroCase` to `true` in the former case, but not in the
773+
* latter.
774+
*/
775+
private predicate unary_compares_eq(
776+
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
743777
) {
744778
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
745-
exists(AbstractValue v | simple_comparison_eq(test, op, k, v) |
779+
exists(AbstractValue v | unary_simple_comparison_eq(test, op, k, inNonZeroCase, v) |
746780
areEqual = true and value = v
747781
or
748782
areEqual = false and value = v.getDualValue()
749783
)
750784
or
751-
complex_eq(test, op, k, areEqual, value)
785+
unary_complex_eq(test, op, k, areEqual, inNonZeroCase, value)
752786
or
753787
/* (x is true => (op == k)) => (!x is false => (op == k)) */
754-
exists(AbstractValue dual | value = dual.getDualValue() |
755-
compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, areEqual, dual)
788+
exists(AbstractValue dual, boolean inNonZeroCase0 |
789+
value = dual.getDualValue() and
790+
unary_compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, inNonZeroCase0, areEqual, dual)
791+
|
792+
k = 0 and inNonZeroCase = inNonZeroCase0
793+
or
794+
k != 0 and inNonZeroCase = true
756795
)
757796
or
758797
// ((test is `areEqual` => op == const + k2) and const == `k1`) =>
759798
// test is `areEqual` => op == k1 + k2
799+
inNonZeroCase = false and
760800
exists(int k1, int k2, ConstantInstruction const |
761801
compares_eq(test, op, const.getAUse(), k2, areEqual, value) and
762802
int_value(const) = k1 and
@@ -781,35 +821,53 @@ private predicate simple_comparison_eq(
781821
value.(BooleanValue).getValue() = false
782822
}
783823

784-
/** Rearrange various simple comparisons into `op == k` form. */
785-
private predicate simple_comparison_eq(Instruction test, Operand op, int k, AbstractValue value) {
824+
/**
825+
* Holds if `test` is an instruction that is part of test that eventually is
826+
* used in a conditional branch.
827+
*/
828+
private predicate relevantUnaryComparison(Instruction test) {
829+
not test instanceof CompareInstruction and
830+
exists(IRType type, ConditionalBranchInstruction branch |
831+
type instanceof IRAddressType or type instanceof IRIntegerType
832+
|
833+
type = test.getResultIRType() and
834+
branch.getCondition() = test
835+
)
836+
or
837+
exists(LogicalNotInstruction logicalNot |
838+
relevantUnaryComparison(logicalNot) and
839+
test = logicalNot.getUnary()
840+
)
841+
}
842+
843+
/**
844+
* Rearrange various simple comparisons into `op == k` form.
845+
*/
846+
private predicate unary_simple_comparison_eq(
847+
Instruction test, Operand op, int k, boolean inNonZeroCase, AbstractValue value
848+
) {
786849
exists(SwitchInstruction switch, CaseEdge case |
787850
test = switch.getExpression() and
788851
op.getDef() = test and
789852
case = value.(MatchValue).getCase() and
790853
exists(switch.getSuccessor(case)) and
791-
case.getValue().toInt() = k
854+
case.getValue().toInt() = k and
855+
inNonZeroCase = false
792856
)
793857
or
794858
// There's no implicit CompareInstruction in files compiled as C since C
795859
// doesn't have implicit boolean conversions. So instead we check whether
796860
// there's a branch on a value of pointer or integer type.
797-
exists(ConditionalBranchInstruction branch, IRType type |
798-
not test instanceof CompareInstruction and
799-
type = test.getResultIRType() and
800-
(type instanceof IRAddressType or type instanceof IRIntegerType) and
801-
test = branch.getCondition() and
802-
op.getDef() = test
803-
|
804-
// We'd like to also include a case such as:
805-
// ```
806-
// k = 1 and
807-
// value.(BooleanValue).getValue() = true
808-
// ```
809-
// but all we know is that the value is non-zero in the true branch.
810-
// So we can only conclude something in the false branch.
861+
relevantUnaryComparison(test) and
862+
op.getDef() = test and
863+
(
864+
k = 1 and
865+
value.(BooleanValue).getValue() = true and
866+
inNonZeroCase = true
867+
or
811868
k = 0 and
812-
value.(BooleanValue).getValue() = false
869+
value.(BooleanValue).getValue() = false and
870+
inNonZeroCase = false
813871
)
814872
}
815873

@@ -821,12 +879,12 @@ private predicate complex_eq(
821879
add_eq(cmp, left, right, k, areEqual, value)
822880
}
823881

824-
private predicate complex_eq(
825-
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
882+
private predicate unary_complex_eq(
883+
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
826884
) {
827-
sub_eq(test, op, k, areEqual, value)
885+
unary_sub_eq(test, op, k, areEqual, inNonZeroCase, value)
828886
or
829-
add_eq(test, op, k, areEqual, value)
887+
unary_add_eq(test, op, k, areEqual, inNonZeroCase, value)
830888
}
831889

832890
/*
@@ -1090,16 +1148,20 @@ private predicate sub_eq(
10901148
}
10911149

10921150
// op - x == c => op == (c+x)
1093-
private predicate sub_eq(Instruction test, Operand op, int k, boolean areEqual, AbstractValue value) {
1151+
private predicate unary_sub_eq(
1152+
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
1153+
) {
1154+
inNonZeroCase = false and
10941155
exists(SubInstruction sub, int c, int x |
1095-
compares_eq(test, sub.getAUse(), c, areEqual, value) and
1156+
unary_compares_eq(test, sub.getAUse(), c, areEqual, inNonZeroCase, value) and
10961157
op = sub.getLeftOperand() and
10971158
x = int_value(sub.getRight()) and
10981159
k = c + x
10991160
)
11001161
or
1162+
inNonZeroCase = false and
11011163
exists(PointerSubInstruction sub, int c, int x |
1102-
compares_eq(test, sub.getAUse(), c, areEqual, value) and
1164+
unary_compares_eq(test, sub.getAUse(), c, areEqual, inNonZeroCase, value) and
11031165
op = sub.getLeftOperand() and
11041166
x = int_value(sub.getRight()) and
11051167
k = c + x
@@ -1153,11 +1215,13 @@ private predicate add_eq(
11531215
}
11541216

11551217
// left + x == right + c => left == right + (c-x)
1156-
private predicate add_eq(
1157-
Instruction test, Operand left, int k, boolean areEqual, AbstractValue value
1218+
private predicate unary_add_eq(
1219+
Instruction test, Operand left, int k, boolean areEqual, boolean inNonZeroCase,
1220+
AbstractValue value
11581221
) {
1222+
inNonZeroCase = false and
11591223
exists(AddInstruction lhs, int c, int x |
1160-
compares_eq(test, lhs.getAUse(), c, areEqual, value) and
1224+
unary_compares_eq(test, lhs.getAUse(), c, areEqual, inNonZeroCase, value) and
11611225
(
11621226
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
11631227
or
@@ -1166,8 +1230,9 @@ private predicate add_eq(
11661230
k = c - x
11671231
)
11681232
or
1233+
inNonZeroCase = false and
11691234
exists(PointerAddInstruction lhs, int c, int x |
1170-
compares_eq(test, lhs.getAUse(), c, areEqual, value) and
1235+
unary_compares_eq(test, lhs.getAUse(), c, areEqual, inNonZeroCase, value) and
11711236
(
11721237
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
11731238
or

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ module ProductFlow {
546546
Flow1::PathGraph::edges(pred1, succ1, _, _) and
547547
exists(ReturnKindExt returnKind |
548548
succ1.getNode() = returnKind.getAnOutNode(call) and
549-
pred1.getNode().(ReturnNodeExt).getKind() = returnKind
549+
paramReturnNode(_, pred1.asParameterReturnNode(), _, returnKind)
550550
)
551551
}
552552

@@ -574,7 +574,7 @@ module ProductFlow {
574574
Flow2::PathGraph::edges(pred2, succ2, _, _) and
575575
exists(ReturnKindExt returnKind |
576576
succ2.getNode() = returnKind.getAnOutNode(call) and
577-
pred2.getNode().(ReturnNodeExt).getKind() = returnKind
577+
paramReturnNode(_, pred2.asParameterReturnNode(), _, returnKind)
578578
)
579579
}
580580

cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,19 @@ class AllocaCall extends FunctionCall {
3737
}
3838
}
3939

40+
/**
41+
* Gets an expression associated with a dataflow node.
42+
*/
43+
private Expr getExpr(DataFlow::Node node) {
44+
result = node.asInstruction().getAst()
45+
or
46+
result = node.asOperand().getUse().getAst()
47+
or
48+
result = node.(DataFlow::RawIndirectInstruction).getInstruction().getAst()
49+
or
50+
result = node.(DataFlow::RawIndirectOperand).getOperand().getUse().getAst()
51+
}
52+
4053
/**
4154
* A loop that contains an `alloca` call.
4255
*/
@@ -185,19 +198,6 @@ class LoopWithAlloca extends Stmt {
185198
not this.conditionReachesWithoutUpdate(var, this.(Loop).getCondition())
186199
}
187200

188-
/**
189-
* Gets an expression associated with a dataflow node.
190-
*/
191-
private Expr getExpr(DataFlow::Node node) {
192-
result = node.asInstruction().getAst()
193-
or
194-
result = node.asOperand().getUse().getAst()
195-
or
196-
result = node.(DataFlow::RawIndirectInstruction).getInstruction().getAst()
197-
or
198-
result = node.(DataFlow::RawIndirectOperand).getOperand().getUse().getAst()
199-
}
200-
201201
/**
202202
* Gets a definition that may be the most recent definition of the
203203
* controlling variable `var` before this loop.
@@ -210,7 +210,7 @@ class LoopWithAlloca extends Stmt {
210210
// Phi nodes will be preceded by nodes that represent actual definitions
211211
not result instanceof DataFlow::SsaPhiNode and
212212
// A source is outside the loop if it's not inside the loop
213-
not exists(Expr e | e = this.getExpr(result) | this = getAnEnclosingLoopOfExpr(e))
213+
not exists(Expr e | e = getExpr(result) | this = getAnEnclosingLoopOfExpr(e))
214214
)
215215
}
216216

@@ -221,9 +221,9 @@ class LoopWithAlloca extends Stmt {
221221
private int getAControllingVarInitialValue(Variable var, DataFlow::Node source) {
222222
source = this.getAPrecedingDef(var) and
223223
(
224-
result = this.getExpr(source).getValue().toInt()
224+
result = getExpr(source).getValue().toInt()
225225
or
226-
result = this.getExpr(source).(Assignment).getRValue().getValue().toInt()
226+
result = getExpr(source).(Assignment).getRValue().getValue().toInt()
227227
)
228228
}
229229

cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ class SnprintfSizeExpr extends BufferAccess, FunctionCall {
107107
}
108108

109109
class MemcmpSizeExpr extends BufferAccess, FunctionCall {
110-
MemcmpSizeExpr() { this.getTarget().hasName("Memcmp") }
110+
MemcmpSizeExpr() { this.getTarget().hasName("memcmp") }
111111

112112
override Expr getPointer() {
113113
result = this.getArgument(0) or

cpp/ql/src/Security/CWE/CWE-416/UseOfUniquePointerAfterLifetimeEnds.ql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ where
3030
outlivesFullExpr(c) and
3131
not c.isFromUninstantiatedTemplate(_) and
3232
isUniquePointerDerefFunction(c.getTarget()) and
33+
// Exclude cases where the pointer is implicitly converted to a non-pointer type
34+
not c.getActualType() instanceof IntegralType and
3335
isTemporary(c.getQualifier().getFullyConverted())
3436
select c,
3537
"The underlying unique pointer object is destroyed after the call to '" + c.getTarget() +
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The "Use of unique pointer after lifetime ends" query (`cpp/use-of-unique-pointer-after-lifetime-ends`) no longer reports an alert when the pointer is converted to a boolean
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
char * create (int arg) {
2+
if (arg > 42) {
3+
// this function may return NULL
4+
return NULL;
5+
}
6+
char * r = malloc(arg);
7+
snprintf(r, arg -1, "Hello");
8+
return r;
9+
}
10+
11+
void process(char *str) {
12+
// str is dereferenced
13+
if (str[0] == 'H') {
14+
printf("Hello H\n");
15+
}
16+
}
17+
18+
void test(int arg) {
19+
// first function returns a pointer that may be NULL
20+
char *str = create(arg);
21+
// str is not checked for nullness before being passed to process function
22+
process(str);
23+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>This rule finds a dereference of a function parameter, whose value comes from another function call that may return NULL, without checks in the meantime.</p>
8+
</overview>
9+
10+
<recommendation>
11+
<p>A check should be added between the return of the function which may return NULL, and its use by the function dereferencing ths pointer.</p>
12+
</recommendation>
13+
14+
<example>
15+
<sample src="DerefNullResult.cpp" />
16+
</example>
17+
18+
<references>
19+
<li>
20+
<a href="https://www.owasp.org/index.php/Null_Dereference">
21+
Null Dereference
22+
</a>
23+
</li>
24+
</references>
25+
26+
</qhelp>

0 commit comments

Comments
 (0)