Skip to content

Commit 7ff4ebd

Browse files
authored
Merge pull request github#14102 from alexet/alexet/remove-unreachable-ir
CPP: Remove sucessors of non-returning IR calls transitively.
2 parents 109bd90 + 5892939 commit 7ff4ebd

File tree

13 files changed

+13394
-7
lines changed

13 files changed

+13394
-7
lines changed
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/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

cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/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]
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR
22
import semmle.code.cpp.ir.implementation.unaliased_ssa.constant.ConstantAnalysis as ConstantAnalysis
3+
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The queries `cpp/double-free` and `cpp/use-after-free` find fewer false positives
5+
in cases where a non-returning function is called.

cpp/ql/test/library-tests/ir/ir/PrintAST.expected

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15740,6 +15740,112 @@ ir.cpp:
1574015740
# 2072| Value = [VariableAccess] 116
1574115741
# 2072| ValueCategory = prvalue(load)
1574215742
# 2073| getStmt(2): [ReturnStmt] return ...
15743+
# 2075| [TopLevelFunction] void exit(int)
15744+
# 2075| <params>:
15745+
# 2075| getParameter(0): [Parameter] code
15746+
# 2075| Type = [IntType] int
15747+
# 2077| [TopLevelFunction] int NonExit()
15748+
# 2077| <params>:
15749+
# 2077| getEntryPoint(): [BlockStmt] { ... }
15750+
# 2078| getStmt(0): [DeclStmt] declaration
15751+
# 2078| getDeclarationEntry(0): [VariableDeclarationEntry] definition of x
15752+
# 2078| Type = [IntType] int
15753+
# 2078| getVariable().getInitializer(): [Initializer] initializer for x
15754+
# 2078| getExpr(): [FunctionCall] call to Add
15755+
# 2078| Type = [IntType] int
15756+
# 2078| ValueCategory = prvalue
15757+
# 2078| getArgument(0): [Literal] 3
15758+
# 2078| Type = [IntType] int
15759+
# 2078| Value = [Literal] 3
15760+
# 2078| ValueCategory = prvalue
15761+
# 2078| getArgument(1): [Literal] 4
15762+
# 2078| Type = [IntType] int
15763+
# 2078| Value = [Literal] 4
15764+
# 2078| ValueCategory = prvalue
15765+
# 2079| getStmt(1): [IfStmt] if (...) ...
15766+
# 2079| getCondition(): [EQExpr] ... == ...
15767+
# 2079| Type = [BoolType] bool
15768+
# 2079| ValueCategory = prvalue
15769+
# 2079| getLeftOperand(): [VariableAccess] x
15770+
# 2079| Type = [IntType] int
15771+
# 2079| ValueCategory = prvalue(load)
15772+
# 2079| getRightOperand(): [Literal] 7
15773+
# 2079| Type = [IntType] int
15774+
# 2079| Value = [Literal] 7
15775+
# 2079| ValueCategory = prvalue
15776+
# 2080| getThen(): [ExprStmt] ExprStmt
15777+
# 2080| getExpr(): [FunctionCall] call to exit
15778+
# 2080| Type = [VoidType] void
15779+
# 2080| ValueCategory = prvalue
15780+
# 2080| getArgument(0): [Literal] 3
15781+
# 2080| Type = [IntType] int
15782+
# 2080| Value = [Literal] 3
15783+
# 2080| ValueCategory = prvalue
15784+
# 2081| getStmt(2): [ExprStmt] ExprStmt
15785+
# 2081| getExpr(): [FunctionCall] call to VoidFunc
15786+
# 2081| Type = [VoidType] void
15787+
# 2081| ValueCategory = prvalue
15788+
# 2082| getStmt(3): [ReturnStmt] return ...
15789+
# 2082| getExpr(): [VariableAccess] x
15790+
# 2082| Type = [IntType] int
15791+
# 2082| ValueCategory = prvalue(load)
15792+
# 2085| [TopLevelFunction] void CallsNonExit()
15793+
# 2085| <params>:
15794+
# 2085| getEntryPoint(): [BlockStmt] { ... }
15795+
# 2086| getStmt(0): [ExprStmt] ExprStmt
15796+
# 2086| getExpr(): [FunctionCall] call to VoidFunc
15797+
# 2086| Type = [VoidType] void
15798+
# 2086| ValueCategory = prvalue
15799+
# 2087| getStmt(1): [ExprStmt] ExprStmt
15800+
# 2087| getExpr(): [FunctionCall] call to exit
15801+
# 2087| Type = [VoidType] void
15802+
# 2087| ValueCategory = prvalue
15803+
# 2087| getArgument(0): [Literal] 3
15804+
# 2087| Type = [IntType] int
15805+
# 2087| Value = [Literal] 3
15806+
# 2087| ValueCategory = prvalue
15807+
# 2088| getStmt(2): [ReturnStmt] return ...
15808+
# 2090| [TopLevelFunction] int TransNonExit()
15809+
# 2090| <params>:
15810+
# 2090| getEntryPoint(): [BlockStmt] { ... }
15811+
# 2091| getStmt(0): [DeclStmt] declaration
15812+
# 2091| getDeclarationEntry(0): [VariableDeclarationEntry] definition of x
15813+
# 2091| Type = [IntType] int
15814+
# 2091| getVariable().getInitializer(): [Initializer] initializer for x
15815+
# 2091| getExpr(): [FunctionCall] call to Add
15816+
# 2091| Type = [IntType] int
15817+
# 2091| ValueCategory = prvalue
15818+
# 2091| getArgument(0): [Literal] 3
15819+
# 2091| Type = [IntType] int
15820+
# 2091| Value = [Literal] 3
15821+
# 2091| ValueCategory = prvalue
15822+
# 2091| getArgument(1): [Literal] 4
15823+
# 2091| Type = [IntType] int
15824+
# 2091| Value = [Literal] 4
15825+
# 2091| ValueCategory = prvalue
15826+
# 2092| getStmt(1): [IfStmt] if (...) ...
15827+
# 2092| getCondition(): [EQExpr] ... == ...
15828+
# 2092| Type = [BoolType] bool
15829+
# 2092| ValueCategory = prvalue
15830+
# 2092| getLeftOperand(): [VariableAccess] x
15831+
# 2092| Type = [IntType] int
15832+
# 2092| ValueCategory = prvalue(load)
15833+
# 2092| getRightOperand(): [Literal] 7
15834+
# 2092| Type = [IntType] int
15835+
# 2092| Value = [Literal] 7
15836+
# 2092| ValueCategory = prvalue
15837+
# 2093| getThen(): [ExprStmt] ExprStmt
15838+
# 2093| getExpr(): [FunctionCall] call to CallsNonExit
15839+
# 2093| Type = [VoidType] void
15840+
# 2093| ValueCategory = prvalue
15841+
# 2094| getStmt(2): [ExprStmt] ExprStmt
15842+
# 2094| getExpr(): [FunctionCall] call to VoidFunc
15843+
# 2094| Type = [VoidType] void
15844+
# 2094| ValueCategory = prvalue
15845+
# 2095| getStmt(3): [ReturnStmt] return ...
15846+
# 2095| getExpr(): [VariableAccess] x
15847+
# 2095| Type = [IntType] int
15848+
# 2095| ValueCategory = prvalue(load)
1574315849
perf-regression.cpp:
1574415850
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
1574515851
# 4| <params>:

0 commit comments

Comments
 (0)