Skip to content

Commit becb469

Browse files
committed
Merge branch 'main' into deduplicate-dataflow-results-take-3
2 parents 2bed77d + 1b90216 commit becb469

File tree

90 files changed

+14569
-307
lines changed

Some content is hidden

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

90 files changed

+14569
-307
lines changed

cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
<ItemGroup>
1212
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
1313
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
14-
<PackageReference Include="xunit" Version="2.5.0" />
15-
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
14+
<PackageReference Include="xunit" Version="2.4.2" />
15+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
1616
<PrivateAssets>all</PrivateAssets>
1717
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
1818
</PackageReference>
19-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
19+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
2020
</ItemGroup>
2121

2222
<ItemGroup>

cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
</ItemGroup>
1818

1919
<ItemGroup>
20-
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
20+
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
2121
</ItemGroup>
2222

2323
<ItemGroup>
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 `DataFlow::asDefiningArgument` predicate now takes its argument from the range starting at `1` instead of `2`. Queries that depend on the single-parameter version of `DataFlow::asDefiningArgument` should have their arguments updated accordingly.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Treat functions that reach the end of the function as returning in the IR.
5+
They used to be treated as unreachable but it is allowed in C.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Functions that do not return due to calling functions that don't return (e.g. `exit`) are now detected as
5+
non-returning in the IR and dataflow.

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,7 @@ class Node extends TIRDataFlowNode {
275275
* after the `f` has returned.
276276
*/
277277
Expr asDefiningArgument(int index) {
278-
// Subtract one because `DefinitionByReferenceNode` is defined to be in
279-
// the range `[0 ... n - 1]` for some `n` instead of `[1 ... n]`.
280-
this.(DefinitionByReferenceNode).getIndirectionIndex() = index - 1 and
278+
this.(DefinitionByReferenceNode).getIndirectionIndex() = index and
281279
result = this.(DefinitionByReferenceNode).getArgument()
282280
}
283281

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,6 @@ predicate hasUnreachedInstruction(IRFunction func) {
405405
exists(Call c |
406406
c.getEnclosingFunction() = func.getFunction() and
407407
any(Options opt).exits(c.getTarget())
408-
) and
409-
not exists(TranslatedUnreachableReturnStmt return |
410-
return.getEnclosingFunction().getFunction() = func.getFunction()
411408
)
412409
}
413410

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -442,29 +442,26 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
442442

443443
/**
444444
* The IR translation of an implicit `return` statement generated by the extractor to handle control
445-
* flow that reaches the end of a non-`void`-returning function body. Since such control flow
446-
* produces undefined behavior, we simply generate an `Unreached` instruction to prevent that flow
447-
* from continuing on to pollute other analysis. The assumption is that the developer is certain
448-
* that the implicit `return` is unreachable, even if the compiler cannot prove it.
445+
* flow that reaches the end of a non-`void`-returning function body. Such control flow
446+
* produces undefined behavior in C++ but not in C. However even in C using the return value is
447+
* undefined behaviour. We make it return uninitialized memory to get as much flow as possible.
449448
*/
450-
class TranslatedUnreachableReturnStmt extends TranslatedReturnStmt {
451-
TranslatedUnreachableReturnStmt() {
449+
class TranslatedNoValueReturnStmt extends TranslatedReturnStmt, TranslatedVariableInitialization {
450+
TranslatedNoValueReturnStmt() {
452451
not stmt.hasExpr() and hasReturnValue(stmt.getEnclosingFunction())
453452
}
454453

455-
override TranslatedElement getChild(int id) { none() }
456-
457-
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
458-
459-
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
460-
tag = OnlyInstructionTag() and
461-
opcode instanceof Opcode::Unreached and
462-
resultType = getVoidType()
454+
final override Instruction getInitializationSuccessor() {
455+
result = this.getEnclosingFunction().getReturnSuccessorInstruction()
463456
}
464457

465-
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
458+
final override Type getTargetType() { result = this.getEnclosingFunction().getReturnType() }
466459

467-
override Instruction getChildSuccessor(TranslatedElement child) { none() }
460+
final override TranslatedInitialization getInitialization() { none() }
461+
462+
final override IRVariable getIRVariable() {
463+
result = this.getEnclosingFunction().getReturnVariable()
464+
}
468465
}
469466

470467
/**

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlock.qll

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,65 @@ predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
1010
or
1111
instr.getSuccessor(kind) instanceof UnreachedInstruction and
1212
kind instanceof GotoEdge
13+
or
14+
isCallToNonReturningFunction(instr) and exists(instr.getSuccessor(kind))
15+
}
16+
17+
/**
18+
* Holds if all calls to `f` never return (e.g. they call `exit` or loop forever)
19+
*/
20+
private predicate isNonReturningFunction(IRFunction f) {
21+
// If the function has an instruction with a missing successor then
22+
// the analysis is probably going to be incorrect, so assume they exit.
23+
not hasInstructionWithMissingSuccessor(f) and
24+
(
25+
// If all flows to the exit block are pass through an unreachable then f never returns.
26+
any(UnreachedInstruction instr).getBlock().postDominates(f.getEntryBlock())
27+
or
28+
// If there is no flow to the exit block then f never returns.
29+
not exists(IRBlock entry, IRBlock exit |
30+
exit = f.getExitFunctionInstruction().getBlock() and
31+
entry = f.getEntryBlock() and
32+
exit = entry.getASuccessor*()
33+
)
34+
or
35+
// If all flows to the exit block are pass through a call that never returns then f never returns.
36+
exists(CallInstruction ci |
37+
ci.getBlock().postDominates(f.getEntryBlock()) and
38+
isCallToNonReturningFunction(ci)
39+
)
40+
)
41+
}
42+
43+
/**
44+
* Holds if `f` has an instruction with a missing successor.
45+
* This matches `instructionWithoutSuccessor` from `IRConsistency`, but
46+
* avoids generating the error strings.
47+
*/
48+
predicate hasInstructionWithMissingSuccessor(IRFunction f) {
49+
exists(Instruction missingSucc |
50+
missingSucc.getEnclosingIRFunction() = f and
51+
not exists(missingSucc.getASuccessor()) and
52+
not missingSucc instanceof ExitFunctionInstruction and
53+
// Phi instructions aren't linked into the instruction-level flow graph.
54+
not missingSucc instanceof PhiInstruction and
55+
not missingSucc instanceof UnreachedInstruction
56+
)
57+
}
58+
59+
/**
60+
* Holds if the call `ci` never returns.
61+
*/
62+
private predicate isCallToNonReturningFunction(CallInstruction ci) {
63+
exists(IRFunction callee, Language::Function staticTarget |
64+
staticTarget = ci.getStaticCallTarget() and
65+
staticTarget = callee.getFunction() and
66+
// We can't easily tell if the call is virtual or not
67+
// if the callee is virtual. So assume that the call is virtual
68+
// if the target is.
69+
not staticTarget.isVirtual() and
70+
isNonReturningFunction(callee)
71+
)
1372
}
1473

1574
pragma[noinline]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
import semmle.code.cpp.ir.implementation.raw.IR as IR
22
import semmle.code.cpp.ir.implementation.raw.constant.ConstantAnalysis as ConstantAnalysis
3+
import semmle.code.cpp.ir.internal.IRCppLanguage as Language

0 commit comments

Comments
 (0)