Skip to content

Commit c01ee59

Browse files
committed
C++: handle calls to noreturn functions
1 parent 383b2e1 commit c01ee59

20 files changed

+277
-6
lines changed

cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TInstruction.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ newtype TInstruction =
1919
) {
2020
IRConstruction::Raw::hasInstruction(tag1, tag2)
2121
} or
22+
TRawUnreachedInstruction(IRFunctionBase irFunc) {
23+
IRConstruction::hasUnreachedInstruction(irFunc)
24+
} or
2225
TUnaliasedSsaPhiInstruction(
2326
TRawInstruction blockStartInstr, UnaliasedSsa::Ssa::MemoryLocation memoryLocation
2427
) {

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ module Raw {
178178
}
179179
}
180180

181-
class TStageInstruction = TRawInstruction;
181+
class TStageInstruction = TRawInstruction or TRawUnreachedInstruction;
182182

183183
predicate hasInstruction(TRawInstruction instr) { any() }
184184

@@ -393,6 +393,16 @@ Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction
393393
.getPrimaryInstructionForSideEffect(getInstructionTag(instruction))
394394
}
395395

396+
predicate hasUnreachedInstruction(IRFunction func) {
397+
exists(Call c |
398+
c.getEnclosingFunction() = func.getFunction() and
399+
(
400+
c.getTarget().hasSpecifier("_Noreturn") or
401+
c.getTarget().getAnAttribute().hasName("noreturn")
402+
)
403+
)
404+
}
405+
396406
import CachedForDebugging
397407

398408
cached

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ newtype TInstructionTag =
3434
CallTargetTag() or
3535
CallTag() or
3636
CallSideEffectTag() or
37+
CallNoReturnTag() or
3738
AllocationSizeTag() or
3839
AllocationElementSizeTag() or
3940
AllocationExtentConvertTag() or

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ abstract class TranslatedCall extends TranslatedExpr {
6666
)
6767
or
6868
child = getSideEffects() and
69-
result = getParent().getChildSuccessor(this)
69+
if this.isNoReturn()
70+
then result = any(UnreachedInstruction instr | this.getEnclosingFunction().getFunction() = instr.getEnclosingFunction())
71+
else result = getParent().getChildSuccessor(this)
7072
}
7173

7274
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -161,6 +163,10 @@ abstract class TranslatedCall extends TranslatedExpr {
161163
*/
162164
abstract predicate hasArguments();
163165

166+
predicate isNoReturn() {
167+
none()
168+
}
169+
164170
final TranslatedSideEffects getSideEffects() { result.getExpr() = expr }
165171
}
166172

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
77
conditionValue = getConstantValue(instr.(ConditionalBranchInstruction).getCondition()) and
88
if conditionValue = 0 then kind instanceof TrueEdge else kind instanceof FalseEdge
99
)
10+
or
11+
instr.getSuccessor(kind) instanceof UnreachedInstruction and
12+
kind instanceof GotoEdge
1013
}
1114

1215
pragma[noinline]
@@ -41,7 +44,9 @@ class ReachableBlock extends IRBlockBase {
4144
* An instruction that is contained in a reachable block.
4245
*/
4346
class ReachableInstruction extends Instruction {
44-
ReachableInstruction() { this.getBlock() instanceof ReachableBlock }
47+
ReachableInstruction() {
48+
this.getBlock() instanceof ReachableBlock and not this instanceof UnreachedInstruction
49+
}
4550
}
4651

4752
module Graph {

cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,13 @@ private module Cached {
3434

3535
cached
3636
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
37-
exists(OldInstruction oldInstruction |
37+
exists(OldIR::Instruction oldInstruction |
3838
irFunc = oldInstruction.getEnclosingIRFunction() and
39-
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
39+
(
40+
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
41+
or
42+
oldInstruction.getOpcode() instanceof Opcode::Unreached
43+
)
4044
)
4145
}
4246

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
77
conditionValue = getConstantValue(instr.(ConditionalBranchInstruction).getCondition()) and
88
if conditionValue = 0 then kind instanceof TrueEdge else kind instanceof FalseEdge
99
)
10+
or
11+
instr.getSuccessor(kind) instanceof UnreachedInstruction and
12+
kind instanceof GotoEdge
1013
}
1114

1215
pragma[noinline]
@@ -41,7 +44,9 @@ class ReachableBlock extends IRBlockBase {
4144
* An instruction that is contained in a reachable block.
4245
*/
4346
class ReachableInstruction extends Instruction {
44-
ReachableInstruction() { this.getBlock() instanceof ReachableBlock }
47+
ReachableInstruction() {
48+
this.getBlock() instanceof ReachableBlock and not this instanceof UnreachedInstruction
49+
}
4550
}
4651

4752
module Graph {

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14408,6 +14408,35 @@ ir.cpp:
1440814408
# 1894| Conversion = [IntegralConversion] integral conversion
1440914409
# 1894| Type = [IntType] int
1441014410
# 1894| ValueCategory = prvalue
14411+
# 1897| [TopLevelFunction] void noreturnFunc()
14412+
# 1897| <params>:
14413+
# 1899| [TopLevelFunction] int noreturnTest(int)
14414+
# 1899| <params>:
14415+
# 1899| getParameter(0): [Parameter] x
14416+
# 1899| Type = [IntType] int
14417+
# 1899| getEntryPoint(): [BlockStmt] { ... }
14418+
# 1900| getStmt(0): [IfStmt] if (...) ...
14419+
# 1900| getCondition(): [LTExpr] ... < ...
14420+
# 1900| Type = [BoolType] bool
14421+
# 1900| ValueCategory = prvalue
14422+
# 1900| getLesserOperand(): [VariableAccess] x
14423+
# 1900| Type = [IntType] int
14424+
# 1900| ValueCategory = prvalue(load)
14425+
# 1900| getGreaterOperand(): [Literal] 10
14426+
# 1900| Type = [IntType] int
14427+
# 1900| Value = [Literal] 10
14428+
# 1900| ValueCategory = prvalue
14429+
# 1900| getThen(): [BlockStmt] { ... }
14430+
# 1901| getStmt(0): [ReturnStmt] return ...
14431+
# 1901| getExpr(): [VariableAccess] x
14432+
# 1901| Type = [IntType] int
14433+
# 1901| ValueCategory = prvalue(load)
14434+
# 1902| getElse(): [BlockStmt] { ... }
14435+
# 1903| getStmt(0): [ExprStmt] ExprStmt
14436+
# 1903| getExpr(): [FunctionCall] call to noreturnFunc
14437+
# 1903| Type = [VoidType] void
14438+
# 1903| ValueCategory = prvalue
14439+
# 1905| getStmt(1): [ReturnStmt] return ...
1441114440
perf-regression.cpp:
1441214441
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
1441314442
# 4| <params>:

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ missingOperandType
66
duplicateChiOperand
77
sideEffectWithoutPrimary
88
instructionWithoutSuccessor
9+
| ir.cpp:1754:41:1754:42 | Chi: call to CopyConstructorTestVirtualClass | Instruction 'Chi: call to CopyConstructorTestVirtualClass' has no successors in function '$@'. | ir.cpp:1750:5:1750:34 | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) |
10+
| ir.cpp:1903:9:1903:20 | Chi: call to noreturnFunc | Instruction 'Chi: call to noreturnFunc' has no successors in function '$@'. | ir.cpp:1899:5:1899:16 | int noreturnTest(int) | int noreturnTest(int) |
911
ambiguousSuccessors
1012
unexplainedLoop
1113
unnecessaryPhiInstruction

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ missingOperandType
66
duplicateChiOperand
77
sideEffectWithoutPrimary
88
instructionWithoutSuccessor
9+
| ir.cpp:1754:41:1754:42 | Chi: call to CopyConstructorTestVirtualClass | Instruction 'Chi: call to CopyConstructorTestVirtualClass' has no successors in function '$@'. | ir.cpp:1750:5:1750:34 | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) |
10+
| ir.cpp:1903:9:1903:20 | Chi: call to noreturnFunc | Instruction 'Chi: call to noreturnFunc' has no successors in function '$@'. | ir.cpp:1899:5:1899:16 | int noreturnTest(int) | int noreturnTest(int) |
911
ambiguousSuccessors
1012
unexplainedLoop
1113
unnecessaryPhiInstruction

0 commit comments

Comments
 (0)