Skip to content

Commit 9d35ff7

Browse files
author
Dave Bartolomeo
committed
C++/C#: Make escape analysis unsound by default
When building SSA, we'll be assuming that stack variables do not escape, at least until we improve our alias analysis. I've added a new `IREscapeAnalysisConfiguration` class to allow the query to control this, and a new `UseSoundEscapeAnalysis.qll` module that can be imported to switch to the sound escape analysis. I've cloned the existing IR and SSA tests to have both sound and unsound versions. There were relatively few diffs in the IR dump tests, and the sanity tests still give the same results after one change described below. Assuming that stack variables do not escape exposed an existing bug where we do not emit an `Uninitialized` instruction for the temporary variables used by `return` statements and `throw` expressions, even if the initializer is a constructor call or array initializer. I've refactored the code for handling elements that initialize a variable to share a common base class. I added a test case for returning an object initialized by constructor call, and ensured that the IR diffs for the existing `throw` test cases are correct.
1 parent 80997a3 commit 9d35ff7

File tree

53 files changed

+14653
-170
lines changed

Some content is hidden

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

53 files changed

+14653
-170
lines changed

config/identical-files.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@
8282
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
8383
"csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll"
8484
],
85+
"IR IRConfiguration": [
86+
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
87+
"csharp/ql/src/semmle/code/csharp/ir/implementation/IRConfiguration.qll"
88+
],
89+
"IR UseSoundEscapeAnalysis": [
90+
"cpp/ql/src/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
91+
"csharp/ql/src/semmle/code/csharp/ir/implementation/UseSoundEscapeAnalysis.qll"
92+
],
8593
"IR Operand Tag": [
8694
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
8795
"csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTag.qll"

cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/**
2+
* Module used to configure the IR generation process.
3+
*/
4+
15
private import internal.IRConfigurationInternal
26

37
private newtype TIRConfiguration = MkIRConfiguration()
@@ -13,3 +17,18 @@ class IRConfiguration extends TIRConfiguration {
1317
*/
1418
predicate shouldCreateIRForFunction(Language::Function func) { any() }
1519
}
20+
21+
private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration()
22+
23+
/**
24+
* The query can extend this class to control what escape analysis is used when generating SSA.
25+
*/
26+
class IREscapeAnalysisConfiguration extends TIREscapeAnalysisConfiguration {
27+
string toString() { result = "IREscapeAnalysisConfiguration" }
28+
29+
/**
30+
* Holds if the escape analysis done by SSA construction should be sound. By default, the SSA is
31+
* built assuming that no variable's address ever escapes.
32+
*/
33+
predicate useSoundEscapeAnalysis() { none() }
34+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import IRConfiguration
2+
3+
/**
4+
* Overrides the default IR configuration to use sound escape analysis, instead of assuming that
5+
* variable addresses never escape.
6+
*/
7+
class SoundEscapeAnalysisConfiguration extends IREscapeAnalysisConfiguration {
8+
override predicate useSoundEscapeAnalysis() { any() }
9+
}

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ private import AliasAnalysisInternal
22
private import cpp
33
private import InputIR
44
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
5+
private import semmle.code.cpp.ir.implementation.IRConfiguration
56
private import semmle.code.cpp.models.interfaces.Alias
67

78
private class IntValue = Ints::IntValue;
@@ -277,9 +278,16 @@ private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
277278
* analysis.
278279
*/
279280
predicate variableAddressEscapes(IRVariable var) {
280-
automaticVariableAddressEscapes(var.(IRAutomaticVariable))
281+
exists(IREscapeAnalysisConfiguration config |
282+
config.useSoundEscapeAnalysis() and
283+
(
284+
automaticVariableAddressEscapes(var.(IRAutomaticVariable))
285+
)
286+
)
281287
or
282-
// All variables with static storage duration have their address escape.
288+
// All variables with static storage duration have their address escape, even when escape analysis
289+
// is allowed to be unsound. Otherwise, we won't have a definition for any non-escaped global
290+
// variable. Normally, we rely on `AliasedDefinition` to handle that.
283291
not var instanceof IRAutomaticVariable
284292
}
285293

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ class PropertyProvider extends IRPropertyProvider {
107107
exists(
108108
MemoryLocation useLocation, IRBlock predBlock, IRBlock defBlock, int defIndex, Overlap overlap
109109
|
110-
hasPhiOperandDefinition(_, useLocation, block, predBlock, defBlock, defIndex, overlap) and
110+
hasPhiOperandDefinition(_, useLocation, block, predBlock, defBlock, defIndex) and
111111
key = "PhiUse[" + useLocation.toString() + " from " + predBlock.getDisplayIndex().toString() +
112112
"]" and
113113
result = defBlock.getDisplayIndex().toString() + "_" + defIndex + " (" + overlap.toString() +

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll

Lines changed: 12 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -43,93 +43,34 @@ abstract class TranslatedDeclarationEntry extends TranslatedElement, TTranslated
4343
* Represents the IR translation of the declaration of a local variable,
4444
* including its initialization, if any.
4545
*/
46-
abstract class TranslatedVariableDeclaration extends TranslatedElement, InitializationContext {
46+
abstract class TranslatedLocalVariableDeclaration extends TranslatedVariableInitialization {
4747
/**
4848
* Gets the local variable being declared.
4949
*/
5050
abstract LocalVariable getVariable();
5151

52-
override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
52+
final override Type getTargetType() { result = getVariableType(getVariable()) }
5353

54-
override Instruction getFirstInstruction() {
55-
result = getInstruction(InitializerVariableAddressTag())
56-
}
57-
58-
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
59-
tag = InitializerVariableAddressTag() and
60-
opcode instanceof Opcode::VariableAddress and
61-
resultType = getTypeForGLValue(getVariableType(getVariable()))
62-
or
63-
hasUninitializedInstruction() and
64-
tag = InitializerStoreTag() and
65-
opcode instanceof Opcode::Uninitialized and
66-
resultType = getTypeForPRValue(getVariableType(getVariable()))
67-
}
68-
69-
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
70-
(
71-
tag = InitializerVariableAddressTag() and
72-
kind instanceof GotoEdge and
73-
if hasUninitializedInstruction()
74-
then result = getInstruction(InitializerStoreTag())
75-
else result = getInitialization().getFirstInstruction()
76-
)
77-
or
78-
hasUninitializedInstruction() and
79-
kind instanceof GotoEdge and
80-
tag = InitializerStoreTag() and
81-
(
82-
result = getInitialization().getFirstInstruction()
83-
or
84-
not exists(getInitialization()) and result = getParent().getChildSuccessor(this)
85-
)
86-
}
87-
88-
override Instruction getChildSuccessor(TranslatedElement child) {
89-
child = getInitialization() and result = getParent().getChildSuccessor(this)
90-
}
91-
92-
override IRVariable getInstructionVariable(InstructionTag tag) {
93-
(
94-
tag = InitializerVariableAddressTag()
95-
or
96-
hasUninitializedInstruction() and tag = InitializerStoreTag()
97-
) and
98-
result = getIRUserVariable(getFunction(), getVariable())
99-
}
100-
101-
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
102-
hasUninitializedInstruction() and
103-
tag = InitializerStoreTag() and
104-
operandTag instanceof AddressOperandTag and
105-
result = getInstruction(InitializerVariableAddressTag())
106-
}
107-
108-
override Instruction getTargetAddress() {
109-
result = getInstruction(InitializerVariableAddressTag())
110-
}
111-
112-
override Type getTargetType() { result = getVariableType(getVariable()) }
113-
114-
private TranslatedInitialization getInitialization() {
54+
final override TranslatedInitialization getInitialization() {
11555
result = getTranslatedInitialization(getVariable()
11656
.getInitializer()
11757
.getExpr()
11858
.getFullyConverted())
11959
}
12060

121-
private predicate hasUninitializedInstruction() {
122-
not exists(getInitialization()) or
123-
getInitialization() instanceof TranslatedListInitialization or
124-
getInitialization() instanceof TranslatedConstructorInitialization or
125-
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
61+
final override Instruction getInitializationSuccessor() {
62+
result = getParent().getChildSuccessor(this)
63+
}
64+
65+
final override IRVariable getIRVariable() {
66+
result = getIRUserVariable(getFunction(), getVariable())
12667
}
12768
}
12869

12970
/**
13071
* Represents the IR translation of a local variable declaration within a declaration statement.
13172
*/
132-
class TranslatedVariableDeclarationEntry extends TranslatedVariableDeclaration,
73+
class TranslatedVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
13374
TranslatedDeclarationEntry {
13475
LocalVariable var;
13576

@@ -151,7 +92,7 @@ TranslatedRangeBasedForVariableDeclaration getTranslatedRangeBasedForVariableDec
15192
/**
15293
* Represents the IR translation of a compiler-generated variable in a range-based `for` loop.
15394
*/
154-
class TranslatedRangeBasedForVariableDeclaration extends TranslatedVariableDeclaration,
95+
class TranslatedRangeBasedForVariableDeclaration extends TranslatedLocalVariableDeclaration,
15596
TTranslatedRangeBasedForVariableDeclaration {
15697
RangeBasedForStmt forStmt;
15798
LocalVariable var;
@@ -181,7 +122,7 @@ TranslatedConditionDecl getTranslatedConditionDecl(ConditionDeclExpr expr) {
181122
* }
182123
* ```
183124
*/
184-
class TranslatedConditionDecl extends TranslatedVariableDeclaration, TTranslatedConditionDecl {
125+
class TranslatedConditionDecl extends TranslatedLocalVariableDeclaration, TTranslatedConditionDecl {
185126
ConditionDeclExpr conditionDeclExpr;
186127

187128
TranslatedConditionDecl() { this = TTranslatedConditionDecl(conditionDeclExpr) }

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1932,47 +1932,33 @@ abstract class TranslatedThrowExpr extends TranslatedNonConstantExpr {
19321932
* IR translation of a `throw` expression with an argument
19331933
* (e.g. `throw std::bad_alloc()`).
19341934
*/
1935-
class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContext {
1935+
class TranslatedThrowValueExpr extends TranslatedThrowExpr, TranslatedVariableInitialization {
19361936
TranslatedThrowValueExpr() { not expr instanceof ReThrowExpr }
19371937

1938-
override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
1939-
1940-
override Instruction getFirstInstruction() {
1941-
result = getInstruction(InitializerVariableAddressTag())
1942-
}
1943-
19441938
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
19451939
TranslatedThrowExpr.super.hasInstruction(opcode, tag, resultType)
19461940
or
1947-
tag = InitializerVariableAddressTag() and
1948-
opcode instanceof Opcode::VariableAddress and
1949-
resultType = getTypeForGLValue(getExceptionType())
1941+
TranslatedVariableInitialization.super.hasInstruction(opcode, tag, resultType)
19501942
}
19511943

19521944
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
19531945
result = TranslatedThrowExpr.super.getInstructionSuccessor(tag, kind)
19541946
or
1955-
tag = InitializerVariableAddressTag() and
1956-
result = getInitialization().getFirstInstruction() and
1957-
kind instanceof GotoEdge
1947+
result = TranslatedVariableInitialization.super.getInstructionSuccessor(tag, kind)
19581948
}
19591949

1960-
override Instruction getChildSuccessor(TranslatedElement child) {
1961-
child = getInitialization() and
1950+
final override Instruction getInitializationSuccessor() {
19621951
result = getInstruction(ThrowTag())
19631952
}
19641953

1965-
override IRVariable getInstructionVariable(InstructionTag tag) {
1966-
tag = InitializerVariableAddressTag() and
1967-
result = getIRTempVariable(expr, ThrowTempVar())
1968-
}
1969-
19701954
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
19711955
tag = ThrowTempVar() and
19721956
type = getTypeForPRValue(getExceptionType())
19731957
}
19741958

19751959
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
1960+
result = TranslatedVariableInitialization.super.getInstructionOperand(tag, operandTag)
1961+
or
19761962
tag = ThrowTag() and
19771963
(
19781964
operandTag instanceof AddressOperandTag and
@@ -1989,16 +1975,16 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex
19891975
result = getTypeForPRValue(getExceptionType())
19901976
}
19911977

1992-
override Instruction getTargetAddress() {
1993-
result = getInstruction(InitializerVariableAddressTag())
1994-
}
1995-
19961978
override Type getTargetType() { result = getExceptionType() }
19971979

1998-
TranslatedInitialization getInitialization() {
1980+
final override TranslatedInitialization getInitialization() {
19991981
result = getTranslatedInitialization(expr.getExpr().getFullyConverted())
20001982
}
20011983

1984+
final override IRVariable getIRVariable() {
1985+
result = getIRTempVariable(expr, ThrowTempVar())
1986+
}
1987+
20021988
final override Opcode getThrowOpcode() { result instanceof Opcode::ThrowValue }
20031989

20041990
private Type getExceptionType() { result = expr.getType() }

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,98 @@ abstract class InitializationContext extends TranslatedElement {
3030
abstract Type getTargetType();
3131
}
3232

33+
/**
34+
* Base class for any element that initializes a stack variable. Examples include local variable
35+
* declarations, `return` statements, and `throw` expressions.
36+
*/
37+
abstract class TranslatedVariableInitialization extends TranslatedElement, InitializationContext {
38+
final override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
39+
40+
final override Instruction getFirstInstruction() {
41+
result = getInstruction(InitializerVariableAddressTag())
42+
}
43+
44+
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
45+
tag = InitializerVariableAddressTag() and
46+
opcode instanceof Opcode::VariableAddress and
47+
resultType = getTypeForGLValue(getTargetType())
48+
or
49+
hasUninitializedInstruction() and
50+
tag = InitializerStoreTag() and
51+
opcode instanceof Opcode::Uninitialized and
52+
resultType = getTypeForPRValue(getTargetType())
53+
}
54+
55+
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
56+
(
57+
tag = InitializerVariableAddressTag() and
58+
kind instanceof GotoEdge and
59+
if hasUninitializedInstruction()
60+
then result = getInstruction(InitializerStoreTag())
61+
else result = getInitialization().getFirstInstruction()
62+
)
63+
or
64+
hasUninitializedInstruction() and
65+
kind instanceof GotoEdge and
66+
tag = InitializerStoreTag() and
67+
(
68+
result = getInitialization().getFirstInstruction()
69+
or
70+
not exists(getInitialization()) and result = getInitializationSuccessor()
71+
)
72+
}
73+
74+
final override Instruction getChildSuccessor(TranslatedElement child) {
75+
child = getInitialization() and result = getInitializationSuccessor()
76+
}
77+
78+
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
79+
hasUninitializedInstruction() and
80+
tag = InitializerStoreTag() and
81+
operandTag instanceof AddressOperandTag and
82+
result = getInstruction(InitializerVariableAddressTag())
83+
}
84+
85+
final override IRVariable getInstructionVariable(InstructionTag tag) {
86+
(
87+
tag = InitializerVariableAddressTag()
88+
or
89+
hasUninitializedInstruction() and tag = InitializerStoreTag()
90+
) and
91+
result = getIRVariable()
92+
}
93+
94+
final override Instruction getTargetAddress() {
95+
result = getInstruction(InitializerVariableAddressTag())
96+
}
97+
98+
/**
99+
* Get the initialization for the variable.
100+
*/
101+
abstract TranslatedInitialization getInitialization();
102+
103+
/**
104+
* Get the `IRVariable` to be initialized. This may be an `IRTempVariable`.
105+
*/
106+
abstract IRVariable getIRVariable();
107+
108+
/**
109+
* Gets the `Instruction` to be executed immediately after the initialization.
110+
*/
111+
abstract Instruction getInitializationSuccessor();
112+
113+
/**
114+
* Holds if this initialization requires an `Uninitialized` instruction to be emitted before
115+
* evaluating the initializer.
116+
*/
117+
final predicate hasUninitializedInstruction() {
118+
not exists(getInitialization()) or
119+
getInitialization() instanceof TranslatedListInitialization or
120+
getInitialization() instanceof TranslatedConstructorInitialization or
121+
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
122+
}
123+
}
124+
33125
/**
34126
* Represents the IR translation of any initialization, whether from an
35127
* initializer list or from a direct initializer.

0 commit comments

Comments
 (0)