Skip to content

Commit 38bd893

Browse files
committed
Merge branch 'main' into no-dtt-in-unbounded-write
2 parents e90803a + 512c6a5 commit 38bd893

File tree

81 files changed

+861
-523
lines changed

Some content is hidden

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

81 files changed

+861
-523
lines changed

cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ abstract class MustFlowConfiguration extends string {
3131
*/
3232
abstract predicate isSink(Operand sink);
3333

34+
/**
35+
* Holds if data flow through `instr` is prohibited.
36+
*/
37+
predicate isBarrier(Instruction instr) { none() }
38+
3439
/**
3540
* Holds if the additional flow step from `node1` to `node2` must be taken
3641
* into account in the analysis.
@@ -48,18 +53,21 @@ abstract class MustFlowConfiguration extends string {
4853
*/
4954
final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) {
5055
this.isSource(source.getInstruction()) and
51-
source.getASuccessor+() = sink
56+
source.getASuccessor*() = sink
5257
}
5358
}
5459

5560
/** Holds if `node` flows from a source. */
5661
pragma[nomagic]
5762
private predicate flowsFromSource(Instruction node, MustFlowConfiguration config) {
58-
config.isSource(node)
59-
or
60-
exists(Instruction mid |
61-
step(mid, node, config) and
62-
flowsFromSource(mid, pragma[only_bind_into](config))
63+
not config.isBarrier(node) and
64+
(
65+
config.isSource(node)
66+
or
67+
exists(Instruction mid |
68+
step(mid, node, config) and
69+
flowsFromSource(mid, pragma[only_bind_into](config))
70+
)
6371
)
6472
}
6573

cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticCFG.qll

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ class SemBasicBlock extends Specific::BasicBlock {
1212
/** Holds if this block (transitively) dominates `otherblock`. */
1313
final predicate bbDominates(SemBasicBlock otherBlock) { Specific::bbDominates(this, otherBlock) }
1414

15-
/** Holds if this block has dominance information. */
16-
final predicate hasDominanceInformation() { Specific::hasDominanceInformation(this) }
17-
1815
/** Gets an expression that is evaluated in this basic block. */
1916
final SemExpr getAnExpr() { result.getBasicBlock() = this }
2017

cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticExprSpecific.qll

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,6 @@ module SemanticExprConfig {
122122
dominator.dominates(dominated)
123123
}
124124

125-
predicate hasDominanceInformation(BasicBlock block) { any() }
126-
127125
private predicate id(Cpp::Locatable x, Cpp::Locatable y) { x = y }
128126

129127
private predicate idOf(Cpp::Locatable x, int y) = equivalenceRelation(id/2)(x, y)
@@ -132,17 +130,7 @@ module SemanticExprConfig {
132130

133131
newtype TSsaVariable =
134132
TSsaInstruction(IR::Instruction instr) { instr.hasMemoryResult() } or
135-
TSsaOperand(IR::Operand op) { op.isDefinitionInexact() } or
136-
TSsaPointerArithmeticGuard(ValueNumber instr) {
137-
exists(Guard g, IR::Operand use |
138-
use = instr.getAUse() and use.getIRType() instanceof IR::IRAddressType
139-
|
140-
g.comparesLt(use, _, _, _, _) or
141-
g.comparesLt(_, use, _, _, _) or
142-
g.comparesEq(use, _, _, _, _) or
143-
g.comparesEq(_, use, _, _, _)
144-
)
145-
}
133+
TSsaOperand(IR::Operand op) { op.isDefinitionInexact() }
146134

147135
class SsaVariable extends TSsaVariable {
148136
string toString() { none() }
@@ -151,8 +139,6 @@ module SemanticExprConfig {
151139

152140
IR::Instruction asInstruction() { none() }
153141

154-
ValueNumber asPointerArithGuard() { none() }
155-
156142
IR::Operand asOperand() { none() }
157143
}
158144

@@ -168,18 +154,6 @@ module SemanticExprConfig {
168154
final override IR::Instruction asInstruction() { result = instr }
169155
}
170156

171-
class SsaPointerArithmeticGuard extends SsaVariable, TSsaPointerArithmeticGuard {
172-
ValueNumber vn;
173-
174-
SsaPointerArithmeticGuard() { this = TSsaPointerArithmeticGuard(vn) }
175-
176-
final override string toString() { result = vn.toString() }
177-
178-
final override Location getLocation() { result = vn.getLocation() }
179-
180-
final override ValueNumber asPointerArithGuard() { result = vn }
181-
}
182-
183157
class SsaOperand extends SsaVariable, TSsaOperand {
184158
IR::Operand op;
185159

@@ -212,11 +186,7 @@ module SemanticExprConfig {
212186
)
213187
}
214188

215-
Expr getAUse(SsaVariable v) {
216-
result.(IR::LoadInstruction).getSourceValue() = v.asInstruction()
217-
or
218-
result = v.asPointerArithGuard().getAnInstruction()
219-
}
189+
Expr getAUse(SsaVariable v) { result.(IR::LoadInstruction).getSourceValue() = v.asInstruction() }
220190

221191
SemType getSsaVariableType(SsaVariable v) {
222192
result = getSemanticType(v.asInstruction().getResultIRType())
@@ -255,10 +225,7 @@ module SemanticExprConfig {
255225
final override Location getLocation() { result = block.getLocation() }
256226

257227
final override predicate hasRead(SsaVariable v) {
258-
exists(IR::Operand operand |
259-
operand.getDef() = v.asInstruction() or
260-
operand.getDef() = v.asPointerArithGuard().getAnInstruction()
261-
|
228+
exists(IR::Operand operand | operand.getDef() = v.asInstruction() |
262229
not operand instanceof IR::PhiInputOperand and
263230
operand.getUse().getBlock() = block
264231
)
@@ -276,10 +243,7 @@ module SemanticExprConfig {
276243
final override Location getLocation() { result = succ.getLocation() }
277244

278245
final override predicate hasRead(SsaVariable v) {
279-
exists(IR::PhiInputOperand operand |
280-
operand.getDef() = v.asInstruction() or
281-
operand.getDef() = v.asPointerArithGuard().getAnInstruction()
282-
|
246+
exists(IR::PhiInputOperand operand | operand.getDef() = v.asInstruction() |
283247
operand.getPredecessorBlock() = pred and
284248
operand.getUse().getBlock() = succ
285249
)

cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticGuard.qll

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,32 +35,4 @@ predicate semImplies_v2(SemGuard g1, boolean b1, SemGuard g2, boolean b2) {
3535
Specific::implies_v2(g1, b1, g2, b2)
3636
}
3737

38-
/**
39-
* Holds if `guard` directly controls the position `controlled` with the
40-
* value `testIsTrue`.
41-
*/
42-
pragma[nomagic]
43-
predicate semGuardDirectlyControlsSsaRead(
44-
SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue
45-
) {
46-
guard.directlyControls(controlled.(SemSsaReadPositionBlock).getBlock(), testIsTrue)
47-
or
48-
exists(SemSsaReadPositionPhiInputEdge controlledEdge | controlledEdge = controlled |
49-
guard.directlyControls(controlledEdge.getOrigBlock(), testIsTrue) or
50-
guard.hasBranchEdge(controlledEdge.getOrigBlock(), controlledEdge.getPhiBlock(), testIsTrue)
51-
)
52-
}
53-
54-
/**
55-
* Holds if `guard` controls the position `controlled` with the value `testIsTrue`.
56-
*/
57-
predicate semGuardControlsSsaRead(SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue) {
58-
semGuardDirectlyControlsSsaRead(guard, controlled, testIsTrue)
59-
or
60-
exists(SemGuard guard0, boolean testIsTrue0 |
61-
semImplies_v2(guard0, testIsTrue0, guard, testIsTrue) and
62-
semGuardControlsSsaRead(guard0, controlled, testIsTrue0)
63-
)
64-
}
65-
6638
SemGuard semGetComparisonGuard(SemRelationalExpr e) { result = Specific::comparisonGuard(e) }

cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticSSA.qll

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -63,36 +63,3 @@ class SemSsaReadPositionBlock extends SemSsaReadPosition {
6363

6464
SemExpr getAnExpr() { result = this.getBlock().getAnExpr() }
6565
}
66-
67-
/**
68-
* Holds if `inp` is an input to `phi` along a back edge.
69-
*/
70-
predicate semBackEdge(SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge) {
71-
edge.phiInput(phi, inp) and
72-
// Conservatively assume that every edge is a back edge if we don't have dominance information.
73-
(
74-
phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or
75-
irreducibleSccEdge(edge.getOrigBlock(), phi.getBasicBlock()) or
76-
not edge.getOrigBlock().hasDominanceInformation()
77-
)
78-
}
79-
80-
/**
81-
* Holds if the edge from b1 to b2 is part of a multiple-entry cycle in an irreducible control flow
82-
* graph.
83-
*
84-
* An ireducible control flow graph is one where the usual dominance-based back edge detection does
85-
* not work, because there is a cycle with multiple entry points, meaning there are
86-
* mutually-reachable basic blocks where neither dominates the other. For such a graph, we first
87-
* remove all detectable back-edges using the normal condition that the predecessor block is
88-
* dominated by the successor block, then mark all edges in a cycle in the resulting graph as back
89-
* edges.
90-
*/
91-
private predicate irreducibleSccEdge(SemBasicBlock b1, SemBasicBlock b2) {
92-
trimmedEdge(b1, b2) and trimmedEdge+(b2, b1)
93-
}
94-
95-
private predicate trimmedEdge(SemBasicBlock pred, SemBasicBlock succ) {
96-
pred.getASuccessor() = succ and
97-
not succ.bbDominates(pred)
98-
}

cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/analysis/RangeAnalysisImpl.qll

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,12 @@ module Sem implements Semantic {
7272

7373
class BasicBlock = SemBasicBlock;
7474

75+
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
76+
7577
class Guard = SemGuard;
7678

7779
predicate implies_v2 = semImplies_v2/4;
7880

79-
predicate guardDirectlyControlsSsaRead = semGuardDirectlyControlsSsaRead/3;
80-
81-
predicate guardControlsSsaRead = semGuardControlsSsaRead/3;
82-
8381
class Type = SemType;
8482

8583
class IntegerType = SemIntegerType;
@@ -100,8 +98,6 @@ module Sem implements Semantic {
10098

10199
class SsaReadPositionBlock = SemSsaReadPositionBlock;
102100

103-
predicate backEdge = semBackEdge/3;
104-
105101
predicate conversionCannotOverflow(Type fromType, Type toType) {
106102
SemanticType::conversionCannotOverflow(fromType, toType)
107103
}

cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/analysis/SignAnalysisCommon.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
294294
) {
295295
exists(boolean testIsTrue, SemRelationalExpr comp |
296296
pos.hasReadOfVar(v) and
297-
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
297+
guardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
298298
not unknownSign(lowerbound)
299299
|
300300
testIsTrue = true and
@@ -318,7 +318,7 @@ module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
318318
) {
319319
exists(boolean testIsTrue, SemRelationalExpr comp |
320320
pos.hasReadOfVar(v) and
321-
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
321+
guardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
322322
not unknownSign(upperbound)
323323
|
324324
testIsTrue = true and
@@ -343,7 +343,7 @@ module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
343343
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
344344
exists(SemGuard guard, boolean testIsTrue, boolean polarity, SemExpr e |
345345
pos.hasReadOfVar(pragma[only_bind_into](v)) and
346-
semGuardControlsSsaRead(guard, pragma[only_bind_into](pos), testIsTrue) and
346+
guardControlsSsaRead(guard, pragma[only_bind_into](pos), testIsTrue) and
347347
e = ssaRead(pragma[only_bind_into](v), D::fromInt(0)) and
348348
guard.isEquality(eqbound, e, polarity) and
349349
isEq = polarity.booleanXor(testIsTrue).booleanNot() and

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

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
*/
1414

1515
import cpp
16-
import semmle.code.cpp.controlflow.StackVariableReachability
16+
import semmle.code.cpp.ir.IR
17+
import semmle.code.cpp.ir.dataflow.MustFlow
1718

1819
/**
1920
* Auxiliary predicate: Types that don't require initialization
@@ -33,31 +34,6 @@ predicate allocatedType(Type t) {
3334
allocatedType(t.getUnspecifiedType())
3435
}
3536

36-
/**
37-
* A declaration of a local variable that leaves the
38-
* variable uninitialized.
39-
*/
40-
DeclStmt declWithNoInit(LocalVariable v) {
41-
result.getADeclaration() = v and
42-
not exists(v.getInitializer()) and
43-
/* The type of the variable is not stack-allocated. */
44-
exists(Type t | t = v.getType() | not allocatedType(t))
45-
}
46-
47-
class UninitialisedLocalReachability extends StackVariableReachability {
48-
UninitialisedLocalReachability() { this = "UninitialisedLocal" }
49-
50-
override predicate isSource(ControlFlowNode node, StackVariable v) { node = declWithNoInit(v) }
51-
52-
override predicate isSink(ControlFlowNode node, StackVariable v) { useOfVarActual(v, node) }
53-
54-
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
55-
// only report the _first_ possibly uninitialized use
56-
useOfVarActual(v, node) or
57-
definitionBarrier(v, node)
58-
}
59-
}
60-
6137
pragma[noinline]
6238
predicate containsInlineAssembly(Function f) { exists(AsmStmt s | s.getEnclosingFunction() = f) }
6339

@@ -82,8 +58,33 @@ VariableAccess commonException() {
8258
containsInlineAssembly(result.getEnclosingFunction())
8359
}
8460

85-
from UninitialisedLocalReachability r, LocalVariable v, VariableAccess va
61+
predicate isSinkImpl(Instruction sink, VariableAccess va) {
62+
exists(LoadInstruction load |
63+
va = load.getUnconvertedResultExpression() and
64+
not va = commonException() and
65+
sink = load.getSourceValue()
66+
)
67+
}
68+
69+
class MustFlow extends MustFlowConfiguration {
70+
MustFlow() { this = "MustFlow" }
71+
72+
override predicate isSource(Instruction source) {
73+
source instanceof UninitializedInstruction and
74+
exists(Type t | t = source.getResultType() | not allocatedType(t))
75+
}
76+
77+
override predicate isSink(Operand sink) { isSinkImpl(sink.getDef(), _) }
78+
79+
override predicate allowInterproceduralFlow() { none() }
80+
81+
override predicate isBarrier(Instruction instr) { instr instanceof ChiInstruction }
82+
}
83+
84+
from
85+
VariableAccess va, LocalVariable v, MustFlow conf, MustFlowPathNode source, MustFlowPathNode sink
8686
where
87-
r.reaches(_, v, va) and
88-
not va = commonException()
87+
conf.hasFlowPath(source, sink) and
88+
isSinkImpl(sink.getInstruction(), va) and
89+
v = va.getTarget()
8990
select va, "The variable $@ may not be initialized at this access.", v, v.getName()
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 `cpp/uninitialized-local` query has been improved to produce fewer false positives.

cpp/ql/test/library-tests/ir/range-analysis/test.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,12 @@ void test_div(int x) {
134134
struct X { int n; };
135135
void read_argument(const X *);
136136

137+
// This test exists purely to ensure that modulus analysis terminates in the
138+
// presence of inexact phi operands. The LoadInstruction on `while(x->n) { ... }`
139+
// reads from a PhiInstruction with two input operands: an exact operand defined
140+
// by the StoreInstruction generated by `x->n--` and an inexact operand coming
141+
// from the WriteSideEffect generated by `read_argument(x)`. If we don't consider
142+
// the inexact operand modulus analysis fails to terminate.
137143
void nonterminating_without_operands_as_ssa(X *x) {
138144
read_argument(x);
139145
while (x->n) {

0 commit comments

Comments
 (0)