Skip to content

Commit 93c6f8f

Browse files
authored
Merge pull request github#3056 from dbartol/dbartol/static-locals
C++: Model dynamic initialization of static local variables in IR
2 parents 46567a5 + 309ccf3 commit 93c6f8f

File tree

24 files changed

+693
-100
lines changed

24 files changed

+693
-100
lines changed

cpp/ql/src/semmle/code/cpp/Variable.qll

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -381,10 +381,10 @@ class StaticStorageDurationVariable extends Variable {
381381
}
382382

383383
/**
384-
* Holds if the initializer for this variable is evaluated at compile time.
384+
* Holds if the initializer for this variable is evaluated at runtime.
385385
*/
386-
predicate hasConstantInitialization() {
387-
not runtimeExprInStaticInitializer(this.getInitializer().getExpr())
386+
predicate hasDynamicInitialization() {
387+
runtimeExprInStaticInitializer(this.getInitializer().getExpr())
388388
}
389389
}
390390

@@ -409,6 +409,11 @@ private predicate inStaticInitializer(Expr e) {
409409
inStaticInitializer(e.getParent())
410410
}
411411

412+
/**
413+
* A C++ local variable declared as `static`.
414+
*/
415+
class StaticLocalVariable extends LocalVariable, StaticStorageDurationVariable { }
416+
412417
/**
413418
* A C/C++ variable which has global scope or namespace scope. For example the
414419
* variables `a` and `b` in the following code:

cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,9 +441,9 @@ private Node getControlOrderChildSparse(Node n, int i) {
441441
* thus should not have control flow computed.
442442
*/
443443
private predicate skipInitializer(Initializer init) {
444-
exists(LocalVariable local |
444+
exists(StaticLocalVariable local |
445445
init = local.getInitializer() and
446-
local.(StaticStorageDurationVariable).hasConstantInitialization()
446+
not local.hasDynamicInitialization()
447447
)
448448
}
449449

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
2323
IRVariable() {
2424
this = TIRUserVariable(_, _, func) or
2525
this = TIRTempVariable(func, _, _, _) or
26-
this = TIRStringLiteral(func, _, _, _)
26+
this = TIRStringLiteral(func, _, _, _) or
27+
this = TIRDynamicInitializationFlag(func, _, _)
2728
}
2829

2930
string toString() { none() }
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
149150

150151
IRGeneratedVariable() {
151152
this = TIRTempVariable(func, ast, _, type) or
152-
this = TIRStringLiteral(func, ast, type, _)
153+
this = TIRStringLiteral(func, ast, type, _) or
154+
this = TIRDynamicInitializationFlag(func, ast, type)
153155
}
154156

155157
final override Language::LanguageType getLanguageType() { result = type }
@@ -208,7 +210,7 @@ class IRReturnVariable extends IRTempVariable {
208210
class IRThrowVariable extends IRTempVariable {
209211
IRThrowVariable() { tag = ThrowTempVar() }
210212

211-
override string getBaseString() { result = "#throw" }
213+
final override string getBaseString() { result = "#throw" }
212214
}
213215

214216
/**
@@ -226,7 +228,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
226228
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
227229
}
228230

229-
override string getBaseString() { result = "#string" }
231+
final override string getBaseString() { result = "#string" }
230232

231233
final Language::StringLiteral getLiteral() { result = literal }
232234
}
235+
236+
/**
237+
* A variable generated to track whether a specific non-stack variable has been initialized. This is
238+
* used to model the runtime initialization of static local variables in C++, as well as static
239+
* fields in C#.
240+
*/
241+
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
242+
Language::Variable var;
243+
244+
IRDynamicInitializationFlag() {
245+
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
246+
}
247+
248+
final override string toString() { result = var.toString() + "#init" }
249+
250+
final Language::Variable getVariable() { result = var }
251+
252+
final override string getUniqueId() {
253+
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
254+
}
255+
256+
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
257+
}

cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ newtype TIRVariable =
1010
) {
1111
Construction::hasTempVariable(func, ast, tag, type)
1212
} or
13+
TIRDynamicInitializationFlag(
14+
Language::Function func, Language::Variable var, Language::LanguageType type
15+
) {
16+
Construction::hasDynamicInitializationFlag(func, var, type)
17+
} or
1318
TIRStringLiteral(
1419
Language::Function func, Language::AST ast, Language::LanguageType type,
1520
Language::StringLiteral literal

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
2323
IRVariable() {
2424
this = TIRUserVariable(_, _, func) or
2525
this = TIRTempVariable(func, _, _, _) or
26-
this = TIRStringLiteral(func, _, _, _)
26+
this = TIRStringLiteral(func, _, _, _) or
27+
this = TIRDynamicInitializationFlag(func, _, _)
2728
}
2829

2930
string toString() { none() }
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
149150

150151
IRGeneratedVariable() {
151152
this = TIRTempVariable(func, ast, _, type) or
152-
this = TIRStringLiteral(func, ast, type, _)
153+
this = TIRStringLiteral(func, ast, type, _) or
154+
this = TIRDynamicInitializationFlag(func, ast, type)
153155
}
154156

155157
final override Language::LanguageType getLanguageType() { result = type }
@@ -208,7 +210,7 @@ class IRReturnVariable extends IRTempVariable {
208210
class IRThrowVariable extends IRTempVariable {
209211
IRThrowVariable() { tag = ThrowTempVar() }
210212

211-
override string getBaseString() { result = "#throw" }
213+
final override string getBaseString() { result = "#throw" }
212214
}
213215

214216
/**
@@ -226,7 +228,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
226228
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
227229
}
228230

229-
override string getBaseString() { result = "#string" }
231+
final override string getBaseString() { result = "#string" }
230232

231233
final Language::StringLiteral getLiteral() { result = literal }
232234
}
235+
236+
/**
237+
* A variable generated to track whether a specific non-stack variable has been initialized. This is
238+
* used to model the runtime initialization of static local variables in C++, as well as static
239+
* fields in C#.
240+
*/
241+
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
242+
Language::Variable var;
243+
244+
IRDynamicInitializationFlag() {
245+
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
246+
}
247+
248+
final override string toString() { result = var.toString() + "#init" }
249+
250+
final Language::Variable getVariable() { result = var }
251+
252+
final override string getUniqueId() {
253+
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
254+
}
255+
256+
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
257+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ private module Cached {
5151
getTypeForPRValue(literal.getType()) = type
5252
}
5353

54+
cached
55+
predicate hasDynamicInitializationFlag(Function func, StaticLocalVariable var, CppType type) {
56+
var.getFunction() = func and
57+
var.hasDynamicInitialization() and
58+
type = getBoolType()
59+
}
60+
5461
cached
5562
predicate hasModeledMemoryResult(Instruction instruction) { none() }
5663

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ newtype TInstructionTag =
88
InitializerStoreTag() or
99
InitializerIndirectAddressTag() or
1010
InitializerIndirectStoreTag() or
11+
DynamicInitializationFlagAddressTag() or
12+
DynamicInitializationFlagLoadTag() or
13+
DynamicInitializationConditionalBranchTag() or
14+
DynamicInitializationFlagConstantTag() or
15+
DynamicInitializationFlagStoreTag() or
1116
ZeroPadStringConstantTag() or
1217
ZeroPadStringElementIndexTag() or
1318
ZeroPadStringElementAddressTag() or
@@ -186,4 +191,14 @@ string getInstructionTagId(TInstructionTag tag) {
186191
tag = AsmTag() and result = "Asm"
187192
or
188193
exists(int index | tag = AsmInputTag(index) and result = "AsmInputTag(" + index + ")")
194+
or
195+
tag = DynamicInitializationFlagAddressTag() and result = "DynInitFlagAddr"
196+
or
197+
tag = DynamicInitializationFlagLoadTag() and result = "DynInitFlagLoad"
198+
or
199+
tag = DynamicInitializationConditionalBranchTag() and result = "DynInitCondBranch"
200+
or
201+
tag = DynamicInitializationFlagConstantTag() and result = "DynInitFlagConst"
202+
or
203+
tag = DynamicInitializationFlagStoreTag() and result = "DynInitFlagStore"
189204
}

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

Lines changed: 160 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ private import semmle.code.cpp.ir.internal.IRUtilities
66
private import InstructionTag
77
private import TranslatedElement
88
private import TranslatedExpr
9+
private import TranslatedFunction
910
private import TranslatedInitialization
1011

1112
/**
@@ -66,17 +67,172 @@ abstract class TranslatedLocalVariableDeclaration extends TranslatedVariableInit
6667
}
6768

6869
/**
69-
* Represents the IR translation of a local variable declaration within a declaration statement.
70+
* The IR translation of a local variable declaration within a declaration statement.
7071
*/
71-
class TranslatedVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
72+
class TranslatedAutoVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
7273
TranslatedDeclarationEntry {
73-
LocalVariable var;
74+
StackVariable var;
7475

75-
TranslatedVariableDeclarationEntry() { var = entry.getDeclaration() }
76+
TranslatedAutoVariableDeclarationEntry() { var = entry.getDeclaration() }
7677

7778
override LocalVariable getVariable() { result = var }
7879
}
7980

81+
/**
82+
* The IR translation of the declaration of a static local variable.
83+
* This element generates the logic that determines whether or not the variable has already been
84+
* initialized, and if not, invokes the initializer and sets the dynamic initialization flag for the
85+
* variable. The actual initialization code is handled in
86+
* `TranslatedStaticLocalVariableInitialization`, which is a child of this element.
87+
*
88+
* The generated code to do the initialization only once is:
89+
* ```
90+
* Block 1
91+
* r1225_1(glval<bool>) = VariableAddress[c#init] :
92+
* r1225_2(bool) = Load : &:r1225_1, ~mu1222_4
93+
* v1225_3(void) = ConditionalBranch : r1225_2
94+
* False -> Block 2
95+
* True -> Block 3
96+
*
97+
* Block 2
98+
* r1225_4(glval<int>) = VariableAddress[c] :
99+
* <actual initialization of `c`>
100+
* r1225_8(bool) = Constant[1] :
101+
* mu1225_9(bool) = Store : &:r1225_1, r1225_8
102+
* Goto -> Block 3
103+
*
104+
* Block 3
105+
* ```
106+
*
107+
* Note that the flag variable, `c#init`, is assumed to be zero-initialized at program startup, just
108+
* like any other variable with static storage duration.
109+
*/
110+
class TranslatedStaticLocalVariableDeclarationEntry extends TranslatedDeclarationEntry {
111+
StaticLocalVariable var;
112+
113+
TranslatedStaticLocalVariableDeclarationEntry() { var = entry.getDeclaration() }
114+
115+
final override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
116+
117+
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
118+
tag = DynamicInitializationFlagAddressTag() and
119+
opcode instanceof Opcode::VariableAddress and
120+
type = getBoolGLValueType()
121+
or
122+
tag = DynamicInitializationFlagLoadTag() and
123+
opcode instanceof Opcode::Load and
124+
type = getBoolType()
125+
or
126+
tag = DynamicInitializationConditionalBranchTag() and
127+
opcode instanceof Opcode::ConditionalBranch and
128+
type = getVoidType()
129+
or
130+
tag = DynamicInitializationFlagConstantTag() and
131+
opcode instanceof Opcode::Constant and
132+
type = getBoolType()
133+
or
134+
tag = DynamicInitializationFlagStoreTag() and
135+
opcode instanceof Opcode::Store and
136+
type = getBoolType()
137+
}
138+
139+
final override Instruction getFirstInstruction() {
140+
result = getInstruction(DynamicInitializationFlagAddressTag())
141+
}
142+
143+
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
144+
tag = DynamicInitializationFlagAddressTag() and
145+
kind instanceof GotoEdge and
146+
result = getInstruction(DynamicInitializationFlagLoadTag())
147+
or
148+
tag = DynamicInitializationFlagLoadTag() and
149+
kind instanceof GotoEdge and
150+
result = getInstruction(DynamicInitializationConditionalBranchTag())
151+
or
152+
tag = DynamicInitializationConditionalBranchTag() and
153+
(
154+
kind instanceof TrueEdge and
155+
result = getParent().getChildSuccessor(this)
156+
or
157+
kind instanceof FalseEdge and
158+
result = getInitialization().getFirstInstruction()
159+
)
160+
or
161+
tag = DynamicInitializationFlagConstantTag() and
162+
kind instanceof GotoEdge and
163+
result = getInstruction(DynamicInitializationFlagStoreTag())
164+
or
165+
tag = DynamicInitializationFlagStoreTag() and
166+
kind instanceof GotoEdge and
167+
result = getParent().getChildSuccessor(this)
168+
}
169+
170+
final override Instruction getChildSuccessor(TranslatedElement child) {
171+
child = getInitialization() and
172+
result = getInstruction(DynamicInitializationFlagConstantTag())
173+
}
174+
175+
final override IRDynamicInitializationFlag getInstructionVariable(InstructionTag tag) {
176+
tag = DynamicInitializationFlagAddressTag() and
177+
result.getVariable() = var
178+
}
179+
180+
final override string getInstructionConstantValue(InstructionTag tag) {
181+
tag = DynamicInitializationFlagConstantTag() and result = "1"
182+
}
183+
184+
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
185+
tag = DynamicInitializationFlagLoadTag() and
186+
(
187+
operandTag instanceof AddressOperandTag and
188+
result = getInstruction(DynamicInitializationFlagAddressTag())
189+
or
190+
operandTag instanceof LoadOperandTag and
191+
result = getTranslatedFunction(var.getFunction()).getUnmodeledDefinitionInstruction()
192+
)
193+
or
194+
tag = DynamicInitializationConditionalBranchTag() and
195+
operandTag instanceof ConditionOperandTag and
196+
result = getInstruction(DynamicInitializationFlagLoadTag())
197+
or
198+
tag = DynamicInitializationFlagStoreTag() and
199+
(
200+
operandTag instanceof AddressOperandTag and
201+
result = getInstruction(DynamicInitializationFlagAddressTag())
202+
or
203+
operandTag instanceof StoreValueOperandTag and
204+
result = getInstruction(DynamicInitializationFlagConstantTag())
205+
)
206+
}
207+
208+
private TranslatedStaticLocalVariableInitialization getInitialization() {
209+
result.getVariable() = var
210+
}
211+
}
212+
213+
/**
214+
* The initialization of a static local variable. This element will only exist for a static variable
215+
* with a dynamic initializer.
216+
*/
217+
class TranslatedStaticLocalVariableInitialization extends TranslatedElement,
218+
TranslatedLocalVariableDeclaration, TTranslatedStaticLocalVariableInitialization {
219+
VariableDeclarationEntry entry;
220+
StaticLocalVariable var;
221+
222+
TranslatedStaticLocalVariableInitialization() {
223+
this = TTranslatedStaticLocalVariableInitialization(entry) and
224+
var = entry.getDeclaration()
225+
}
226+
227+
final override string toString() { result = "init: " + entry.toString() }
228+
229+
final override Locatable getAST() { result = entry }
230+
231+
final override LocalVariable getVariable() { result = var }
232+
233+
final override Function getFunction() { result = var.getFunction() }
234+
}
235+
80236
/**
81237
* Gets the `TranslatedRangeBasedForVariableDeclaration` that represents the declaration of
82238
* `var`.

0 commit comments

Comments
 (0)