Skip to content

Commit f19b381

Browse files
committed
C++: Add use-use flow through global variables.
1 parent 74f9b32 commit f19b381

7 files changed

Lines changed: 297 additions & 51 deletions

File tree

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

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -345,21 +345,17 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
345345
*/
346346
predicate jumpStep(Node n1, Node n2) {
347347
exists(Cpp::GlobalOrNamespaceVariable v |
348-
v =
349-
n1.asInstruction()
350-
.(StoreInstruction)
351-
.getResultAddress()
352-
.(VariableAddressInstruction)
353-
.getAstVariable() and
354-
v = n2.asVariable()
348+
exists(Ssa::GlobalUse globalUse |
349+
v = globalUse.getVariable() and
350+
n1.(FinalGlobalValue).getGlobalUse() = globalUse and
351+
v = n2.asVariable(globalUse.getIndirectionIndex())
352+
)
355353
or
356-
v =
357-
n2.asInstruction()
358-
.(LoadInstruction)
359-
.getSourceAddress()
360-
.(VariableAddressInstruction)
361-
.getAstVariable() and
362-
v = n1.asVariable()
354+
exists(Ssa::GlobalDef globalDef |
355+
v = globalDef.getVariable() and
356+
v = n1.asVariable(globalDef.getIndirectionIndex()) and
357+
n2.(InitialGlobalValue).getGlobalDef() = globalDef
358+
)
363359
)
364360
}
365361

@@ -535,7 +531,13 @@ class Unit extends TUnit {
535531
}
536532

537533
/** Holds if `n` should be hidden from path explanations. */
538-
predicate nodeIsHidden(Node n) { n instanceof OperandNode and not n instanceof ArgumentNode }
534+
predicate nodeIsHidden(Node n) {
535+
n instanceof OperandNode and not n instanceof ArgumentNode
536+
or
537+
n instanceof FinalGlobalValue
538+
or
539+
n instanceof InitialGlobalValue
540+
}
539541

540542
class LambdaCallKind = Unit;
541543

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

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ private module Cached {
4242
(not i.getResultType() instanceof VoidType or i.isGLValue())
4343
} or
4444
TOperandNode(Operand op) { not Ssa::ignoreOperand(op) } or
45-
TVariableNode(Variable var) or
45+
TVariableNode(Variable var, int indirectionIndex) {
46+
indirectionIndex = [1 .. Ssa::getMaxIndirectionsForType(var.getUnspecifiedType())]
47+
} or
4648
TPostFieldUpdateNode(FieldAddress operand, int indirectionIndex) {
4749
indirectionIndex =
4850
[1 .. Ssa::countIndirectionsForCppType(operand.getObjectAddress().getResultLanguageType())]
@@ -57,7 +59,9 @@ private module Cached {
5759
} or
5860
TIndirectInstruction(Instruction instr, int indirectionIndex) {
5961
Ssa::hasIndirectInstruction(instr, indirectionIndex)
60-
}
62+
} or
63+
TFinalGlobalValue(Ssa::GlobalUse globalUse) or
64+
TInitialGlobalValue(Ssa::GlobalDef globalUse)
6165
}
6266

6367
/**
@@ -269,7 +273,14 @@ class Node extends TIRDataFlowNode {
269273
* Gets the variable corresponding to this node, if any. This can be used for
270274
* modeling flow in and out of global variables.
271275
*/
272-
Variable asVariable() { result = this.(VariableNode).getVariable() }
276+
Variable asVariable() { result = this.asVariable(1) }
277+
278+
Variable asVariable(int indirectionIndex) {
279+
exists(VariableNode varNode | this = varNode |
280+
varNode.getVariable() = result and
281+
varNode.getIndirectionIndex() = indirectionIndex
282+
)
283+
}
273284

274285
/**
275286
* Gets the expression that is partially defined by this node, if any.
@@ -492,6 +503,42 @@ class SideEffectOperandNode extends Node, IndirectOperand {
492503
Expr getArgument() { result = call.getArgument(argumentIndex).getUnconvertedResultExpression() }
493504
}
494505

506+
class FinalGlobalValue extends Node, TFinalGlobalValue {
507+
Ssa::GlobalUse globalUse;
508+
509+
FinalGlobalValue() { this = TFinalGlobalValue(globalUse) }
510+
511+
Ssa::GlobalUse getGlobalUse() { result = globalUse }
512+
513+
override Declaration getEnclosingCallable() { result = this.getFunction() }
514+
515+
override Declaration getFunction() { result = globalUse.getIRFunction().getFunction() }
516+
517+
override DataFlowType getType() { result instanceof VoidType } // TODO
518+
519+
final override Location getLocationImpl() { result = globalUse.getLocation() }
520+
521+
override string toStringImpl() { result = "FinalGlobalValue" }
522+
}
523+
524+
class InitialGlobalValue extends Node, TInitialGlobalValue {
525+
Ssa::GlobalDef globalDef;
526+
527+
InitialGlobalValue() { this = TInitialGlobalValue(globalDef) }
528+
529+
Ssa::GlobalDef getGlobalDef() { result = globalDef }
530+
531+
override Declaration getEnclosingCallable() { result = this.getFunction() }
532+
533+
override Declaration getFunction() { result = globalDef.getIRFunction().getFunction() }
534+
535+
override DataFlowType getType() { result instanceof VoidType } // TODO
536+
537+
final override Location getLocationImpl() { result = globalDef.getLocation() }
538+
539+
override string toStringImpl() { result = "InitialGlobalValue" }
540+
}
541+
495542
/**
496543
* INTERNAL: do not use.
497544
*
@@ -1074,12 +1121,15 @@ class DefinitionByReferenceNode extends IndirectArgumentOutNode {
10741121
*/
10751122
class VariableNode extends Node, TVariableNode {
10761123
Variable v;
1124+
int indirectionIndex;
10771125

1078-
VariableNode() { this = TVariableNode(v) }
1126+
VariableNode() { this = TVariableNode(v, indirectionIndex) }
10791127

10801128
/** Gets the variable corresponding to this node. */
10811129
Variable getVariable() { result = v }
10821130

1131+
int getIndirectionIndex() { result = indirectionIndex }
1132+
10831133
override Declaration getFunction() { none() }
10841134

10851135
override Declaration getEnclosingCallable() {
@@ -1093,7 +1143,15 @@ class VariableNode extends Node, TVariableNode {
10931143

10941144
override DataFlowType getType() { result = v.getType() }
10951145

1096-
final override Location getLocationImpl() { result = v.getLocation() }
1146+
final override Location getLocationImpl() {
1147+
// Certain variables (such as parameters) can have multiple locations.
1148+
// When there's a unique location we use that one, but if multiple locations
1149+
// exist we default to an unknown location.
1150+
result = unique( | | v.getLocation())
1151+
or
1152+
not exists(unique( | | v.getLocation())) and
1153+
result instanceof UnknownDefaultLocation
1154+
}
10971155

10981156
override string toStringImpl() { result = v.toString() }
10991157
}
@@ -1138,7 +1196,9 @@ DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) {
11381196
}
11391197

11401198
/** Gets the `VariableNode` corresponding to the variable `v`. */
1141-
VariableNode variableNode(Variable v) { result.getVariable() = v }
1199+
VariableNode variableNode(Variable v) {
1200+
result.getVariable() = v and result.getIndirectionIndex() = 1
1201+
}
11421202

11431203
/**
11441204
* DEPRECATED: See UninitializedNode.

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

Lines changed: 158 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,39 @@ predicate hasIndirectInstruction(Instruction instr, int indirectionIndex) {
107107
cached
108108
private newtype TDefOrUseImpl =
109109
TDefImpl(Operand address, int indirectionIndex) {
110-
isDef(_, _, address, _, _, indirectionIndex) and
111-
// We only include the definition if the SSA pruning stage
112-
// concluded that the definition is live after the write.
113-
any(SsaInternals0::Def def).getAddressOperand() = address
110+
exists(Instruction base | isDef(_, _, address, base, _, indirectionIndex) |
111+
// We only include the definition if the SSA pruning stage
112+
// concluded that the definition is live after the write.
113+
any(SsaInternals0::Def def).getAddressOperand() = address
114+
or
115+
// Since the pruning stage doesn't know about global variables we can't use the above check to
116+
// rule out dead assignments to globals.
117+
base.(VariableAddressInstruction).getAstVariable() instanceof Cpp::GlobalOrNamespaceVariable
118+
)
114119
} or
115120
TUseImpl(Operand operand, int indirectionIndex) {
116121
isUse(_, operand, _, _, indirectionIndex) and
117122
not isDef(_, _, operand, _, _, _)
123+
} or
124+
TGlobalUse(Cpp::GlobalOrNamespaceVariable v, IRFunction f, int indirectionIndex) {
125+
// Represents a final "use" of a global variable to ensure that
126+
// the assignment to a global variable isn't ruled out as dead.
127+
exists(VariableAddressInstruction vai, int defIndex |
128+
vai.getEnclosingIRFunction() = f and
129+
vai.getAstVariable() = v and
130+
isDef(_, _, _, vai, _, defIndex) and
131+
indirectionIndex = [0 .. defIndex] + 1
132+
)
133+
} or
134+
TGlobalDef(Cpp::GlobalOrNamespaceVariable v, IRFunction f, int indirectionIndex) {
135+
// Represents the initial "definition" of a global variable when entering
136+
// a function body.
137+
exists(VariableAddressInstruction vai |
138+
vai.getEnclosingIRFunction() = f and
139+
vai.getAstVariable() = v and
140+
isUse(_, _, vai, _, indirectionIndex) and
141+
not isDef(_, _, vai.getAUse(), _, _, _)
142+
)
118143
}
119144

120145
abstract private class DefOrUseImpl extends TDefOrUseImpl {
@@ -254,6 +279,70 @@ class UseImpl extends DefOrUseImpl, TUseImpl {
254279
predicate isCertain() { isUse(true, operand, _, _, ind) }
255280
}
256281

282+
class GlobalUse extends TGlobalUse {
283+
Cpp::GlobalOrNamespaceVariable global;
284+
IRFunction f;
285+
int indirectionIndex;
286+
287+
GlobalUse() { this = TGlobalUse(global, f, indirectionIndex) }
288+
289+
Cpp::GlobalOrNamespaceVariable getVariable() { result = global }
290+
291+
IRFunction getIRFunction() { result = f }
292+
293+
final predicate hasIndexInBlock(IRBlock block, int index) {
294+
exists(ExitFunctionInstruction exit |
295+
exit = f.getExitFunctionInstruction() and
296+
block.getInstruction(index) = exit
297+
)
298+
}
299+
300+
int getIndirectionIndex() { result = indirectionIndex }
301+
302+
SourceVariable getSourceVariable() { sourceVariableIsGlobal(result, global, f, indirectionIndex) }
303+
304+
final predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
305+
this.hasIndexInBlock(block, index) and
306+
sv = this.getSourceVariable()
307+
}
308+
309+
final Cpp::Location getLocation() { result = f.getLocation() }
310+
311+
string toString() { result = global.toString() + " [final value from " + f.toString() + "]" }
312+
}
313+
314+
class GlobalDef extends TGlobalDef {
315+
Cpp::GlobalOrNamespaceVariable global;
316+
IRFunction f;
317+
int indirectionIndex;
318+
319+
GlobalDef() { this = TGlobalDef(global, f, indirectionIndex) }
320+
321+
Cpp::GlobalOrNamespaceVariable getVariable() { result = global }
322+
323+
IRFunction getIRFunction() { result = f }
324+
325+
int getIndirectionIndex() { result = indirectionIndex }
326+
327+
final predicate hasIndexInBlock(IRBlock block, int index) {
328+
exists(EnterFunctionInstruction enter |
329+
enter = f.getEnterFunctionInstruction() and
330+
block.getInstruction(index) = enter
331+
)
332+
}
333+
334+
SourceVariable getSourceVariable() { sourceVariableIsGlobal(result, global, f, indirectionIndex) }
335+
336+
final predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
337+
this.hasIndexInBlock(block, index) and
338+
sv = this.getSourceVariable()
339+
}
340+
341+
final Cpp::Location getLocation() { result = f.getLocation() }
342+
343+
string toString() { result = global.toString() + " [initial value in " + f.toString() + "]" }
344+
}
345+
257346
/**
258347
* Holds if `defOrUse1` is a definition which is first read by `use`,
259348
* or if `defOrUse1` is a use and `use` is a next subsequent use.
@@ -280,6 +369,32 @@ predicate adjacentDefRead(DefOrUse defOrUse1, UseOrPhi use) {
280369
)
281370
}
282371

372+
/**
373+
* Holds if `defOrUse` should flow to the final use of the
374+
* global variable use represetned by `globalUse`.
375+
*/
376+
private predicate defOrUseToGlobalUse(DefOrUse defOrUse, GlobalUse globalUse) {
377+
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, SourceVariable v |
378+
defOrUse.asDefOrUse().hasIndexInBlock(bb1, i1, v) and
379+
globalUse.hasIndexInBlock(bb2, i2, v) and
380+
adjacentDefRead(_, pragma[only_bind_into](bb1), pragma[only_bind_into](i1),
381+
pragma[only_bind_into](bb2), pragma[only_bind_into](i2))
382+
)
383+
}
384+
385+
/**
386+
* Holds if `globalDef` represents the initial definition of a global variable that
387+
* flows to `useOrPhi`.
388+
*/
389+
private predicate globalDefToUse(GlobalDef globalDef, UseOrPhi useOrPhi) {
390+
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, SourceVariable v |
391+
globalDef.hasIndexInBlock(bb1, i1, v) and
392+
adjacentDefRead(_, pragma[only_bind_into](bb1), pragma[only_bind_into](i1),
393+
pragma[only_bind_into](bb2), pragma[only_bind_into](i2)) and
394+
useOrPhi.asDefOrUse().hasIndexInBlock(bb2, i2, v)
395+
)
396+
}
397+
283398
private predicate useToNode(UseOrPhi use, Node nodeTo) {
284399
exists(UseImpl useImpl |
285400
useImpl = use.asDefOrUse() and
@@ -369,6 +484,20 @@ predicate ssaFlow(Node nodeFrom, Node nodeTo) {
369484
adjacentDefRead(defOrUse1, use) and
370485
useToNode(use, nodeTo)
371486
)
487+
or
488+
// Def/use to final value of global vairable
489+
exists(DefOrUse defOrUse, GlobalUse globalUse |
490+
nodeToDefOrUse(nodeFrom, defOrUse) and
491+
defOrUseToGlobalUse(defOrUse, globalUse) and
492+
nodeTo.(FinalGlobalValue).getGlobalUse() = globalUse
493+
)
494+
or
495+
// Initial global variable value to a first use
496+
exists(GlobalDef globalDef, UseOrPhi use |
497+
nodeFrom.(InitialGlobalValue).getGlobalDef() = globalDef and
498+
globalDefToUse(globalDef, use) and
499+
useToNode(use, nodeTo)
500+
)
372501
}
373502

374503
/**
@@ -421,6 +550,17 @@ private predicate variableWriteCand(IRBlock bb, int i, SourceVariable v) {
421550
)
422551
}
423552

553+
private predicate sourceVariableIsGlobal(
554+
SourceVariable sv, Cpp::GlobalOrNamespaceVariable global, IRFunction func, int indirectionIndex
555+
) {
556+
exists(IRVariable irVar, BaseIRVariable base |
557+
sourceVariableHasBaseAndIndex(sv, base, indirectionIndex) and
558+
irVar = base.getIRVariable() and
559+
irVar.getEnclosingIRFunction() = func and
560+
global = irVar.getAst()
561+
)
562+
}
563+
424564
private module SsaInput implements SsaImplCommon::InputSig {
425565
import InputSigCommon
426566
import SourceVariables
@@ -431,10 +571,18 @@ private module SsaInput implements SsaImplCommon::InputSig {
431571
*/
432572
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
433573
DataFlowImplCommon::forceCachingInSameStage() and
434-
variableWriteCand(bb, i, v) and
574+
(
575+
variableWriteCand(bb, i, v) or
576+
sourceVariableIsGlobal(v, _, _, _)
577+
) and
435578
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
436579
if def.isCertain() then certain = true else certain = false
437580
)
581+
or
582+
exists(GlobalDef global |
583+
global.hasIndexInBlock(bb, i, v) and
584+
certain = true
585+
)
438586
}
439587

440588
/**
@@ -445,6 +593,11 @@ private module SsaInput implements SsaImplCommon::InputSig {
445593
exists(UseImpl use | use.hasIndexInBlock(bb, i, v) |
446594
if use.isCertain() then certain = true else certain = false
447595
)
596+
or
597+
exists(GlobalUse global |
598+
global.hasIndexInBlock(bb, i, v) and
599+
certain = true
600+
)
448601
}
449602
}
450603

0 commit comments

Comments
 (0)